diff --git a/cnf/pom.xml b/cnf/pom.xml index c6ed4fc8d14..f413531377d 100644 --- a/cnf/pom.xml +++ b/cnf/pom.xml @@ -84,6 +84,11 @@ commons-math3 3.6.1 + + org.apache.commons + commons-collections4 + 4.3 + org.apache.servicemix.bundles org.apache.servicemix.bundles.xmlrpc-client @@ -94,6 +99,11 @@ org.apache.servicemix.bundles.ws-commons-util 1.0.2_2 + + org.dhatim + fastexcel + 0.10.2 + org.influxdb influxdb-java diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 2ff8f1ccdee..9874fdd54d0 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -8,6 +8,12 @@ ** xref:edge/scheduler.adoc[Scheduler] ** xref:edge/controller.adoc[Controller] ** xref:edge/implement.adoc[Implementing a Device] +** Apps +*** xref:edge/apps/controllers.adoc[Implemented Controllers] +*** xref:edge/apps/devices.adoc[Implemented Devices] +*** xref:edge/apps/schedulers.adoc[Implemented Schedulers] +*** xref:edge/apps/timedata.adoc[Timedata services] +*** xref:edge/apps/utils.adoc[Helpful utilities] ** xref:edge/build.adoc[Build OpenEMS Edge] ** xref:edge/deploy.adoc[Deploy OpenEMS Edge] * OpenEMS UI diff --git a/doc/modules/ROOT/pages/backend/backend-to-backend.adoc b/doc/modules/ROOT/pages/backend/backend-to-backend.adoc index 6e78a046345..918c4cb0738 100644 --- a/doc/modules/ROOT/pages/backend/backend-to-backend.adoc +++ b/doc/modules/ROOT/pages/backend/backend-to-backend.adoc @@ -9,11 +9,11 @@ :source-highlighter: highlight.js :icons: font -OpenEMS Backend provides a "Backend-to-Backend-Api", that provides a way to communicate with one or more OpenEMS Edge devices via backend connection. It is designed as a JSON-RPC communication protocol via Websocket connection. +OpenEMS Backend provides two "Backend-to-Backend-Api" implementations, that provide a way to communicate with one or more OpenEMS Edge devices via backend connection. They are designed using the JSON-RPC communication protocol via REST/JSON (_io.openems.backend.b2brest_) or via Websocket (_io.openems.backend.b2bwebsocket_) connection. == Authentication -BasicAuth on opening of the Websocket connection. +BasicAuth on opening of the connection. == Error Handling @@ -34,6 +34,8 @@ Example: } ---- +NOTE: Properties `id` and `jsonrpc` can be omitted for JSON/REST, as they are not required for HTTP POST calls. + == Requests === GetEdgesStatus diff --git a/doc/modules/ROOT/pages/edge/apps/controllers.adoc b/doc/modules/ROOT/pages/edge/apps/controllers.adoc new file mode 100644 index 00000000000..fadcf2e62da --- /dev/null +++ b/doc/modules/ROOT/pages/edge/apps/controllers.adoc @@ -0,0 +1,186 @@ += Implemented Controllers +:sectnums: +:sectnumlevels: 4 +:toc: +:toclevels: 4 +:experimental: +:keywords: AsciiDoc +:source-highlighter: highlight.js +:icons: font +:imagesdir: ../../../assets/images + +The following control algorithms are available for OpenEMS Edge. + +== Api Backend + +Connects to OpenEMS Backend and sends all Channel data regularly. It is implemented as a Controller, as Channels can be written from OpenEMS Backend. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.api.backend[Source Code icon:github[]] + +== Api Modbus + +Provides a Modbus-Slave implementation for OpenEMS Edge. It provides access to Channels from an external device via Modbus/TCP. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.api.modbus[Source Code icon:github[]] + +== Api REST + +Provides a JSON/REST implementation for OpenEMS Edge. It provides access to Channels and JSON-RPC Requests from an external device via JSON/REST. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.api.rest[Source Code icon:github[]] + +== Api Websocket + +Provides a JSON/REST implementation via HTTP Websocket for OpenEMS Edge. It provides access to Channels and JSON-RPC Requests from an external device via Websocket. This Controller is used for local connection of OpenEMS UI. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.api.websocket[Source Code icon:github[]] + +== Asymmetric Balancing Cos-Phi + +Controls an asymmetric energy storage system in self-consumption optimization mode while keeping the grid meter on a defined cos-phi. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.asymmetric.balancingcosphi[Source Code icon:github[]] + +== Asymmetric Fix Active Power + +Sets a fixed active power for charging/discharging of an asymmetric energy storage system. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.asymmetric.fixactivepower[Source Code icon:github[]] + +== Asymmetric Fix Reactive Power + +Sets a fixed reactive power for an asymmetric energy storage system. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.asymmetric.fixreactivepower[Source Code icon:github[]] + +== Asymmetric Phase Rectification + +Balances the three phases at the grid using an asymmetric energy storage system. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.asymmetric.phaserectification[Source Code icon:github[]] + +== Channel-Threshold + +Generic Controller that sets a digital output according to the value of given Channel - e.g. turn a Relay on, when battery state of charge is above a given threshold. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.channelthreshold[Source Code icon:github[]] + +== CHP control via State-of-Charge (SoC) + +Controls a CHP device. Signals the CHP to turn on when battery SoC is low; signal it to turn off when SoC is high. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.chp.soc[Source Code icon:github[]] + +== Detailed Debug Log + +Constantly shows the values of all Channels of a Component on the console. Primarily used for developing and debugging. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.debug.detailedlog[Source Code icon:github[]] + +== Debug Log + +Constantly shows the most important values of all Components on the console. This is often activated by default to be able to track the running system easily. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.debug.log[Source Code icon:github[]] + +== ESS Limit Total Discharge + +Limits the discharge power of an energy storage system according to its State-of-Charge, e.g. to keep energy for emergency power or to avoid deep discharge. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.ess.limittotaldischarge[Source Code icon:github[]] + +== ESS One Full Cycle + +Executes a full charge/discharge cycle with an energy storage system. This can be used to let the Battery Management System (BMS) reset its reference points for State-of-Charge calculattion. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.ess.onefullcycle[Source Code icon:github[]] + +== EVCS Fix Active Power + +Sets a fixed maximum charge power to an Electric Vehicle Charging Station (EVCS). + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.evcs.fixactivepower[Source Code icon:github[]] + +== EVCS + +Controls an Electric Vehicle Charging Station (EVCS) in different modes, like "Force-Charge" and "Surplus Energy Charging". + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.evcs[Source Code icon:github[]] + +== High-Load Timeslot + +Controls an energy storage system for a High-Load timeslot application (German "Hochlastzeitfenster"). + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.highloadtimeslot[Source Code icon:github[]] + +== IO Alarm + +Switches a digital output, when one or more State-Channels are set. Can be used to signal alarms. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.io.alarm[Source Code icon:github[]] + +== IO Fix Digital Output + +Sets a digital output statically ON or OFF. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.io.fixdigitaloutput[Source Code icon:github[]] + +== PV-Inverter Fix Power Limit + +Sets a fixed power limit for PV-Inverter production. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.pvinverter.fixpowerlimit[Source Code icon:github[]] + +== Symmetric Balancing + +Controls a symmetric energy storage system in self-consumption optimization mode. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.balancing[Source Code icon:github[]] + +== Symmetric Balancing Schedule + +Controls a symmetric energy storage system in self-consumption optimization mode. Allows the definition of a Schedule to set the target power on the grid meter. This Controller can be controlled using the OpenEMS Backend-to-Backend interface. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.balancingschedule[Source Code icon:github[]] + +== Symmetric Fix Active Power + +Sets a fixed active power for charging/discharging of a symmetric energy storage system. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.fixactivepower[Source Code icon:github[]] + +== Symmetric Fix Reactive Power + +Sets a fixed reactive power for a symmetric energy storage system. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.fixreactivepower[Source Code icon:github[]] + +== Symmetric Limit Active Power + +Limits the allowed active power for charging and discharging of a symmetric energy storage system. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.limitactivepower[Source Code icon:github[]] + +== Symmetric Linear Power Band + +Executes a test cycle for a symmetric energy storage system by increasing and decreasing the charging/discharging power in given limits. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.linearpowerband[Source Code icon:github[]] + +== Symmetric Peak-Shaving + +Applies peak-shaving at the grid using a symmetric energy storage system. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.peakshaving[Source Code icon:github[]] + +== Symmetric Random-Power + +Applies random charging/discharging of a symmetric energy storage system for performance tests. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.randompower[Source Code icon:github[]] + +== Symmetric Reactive-Power Voltage-Characterictics + +Controls a symmetric energy storage system using a Q-by-U reference function. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic[Source Code icon:github[]] \ No newline at end of file diff --git a/doc/modules/ROOT/pages/edge/apps/devices.adoc b/doc/modules/ROOT/pages/edge/apps/devices.adoc new file mode 100644 index 00000000000..e54f99b60ae --- /dev/null +++ b/doc/modules/ROOT/pages/edge/apps/devices.adoc @@ -0,0 +1,234 @@ += Implemented Devices +:sectnums: +:sectnumlevels: 4 +:toc: +:toclevels: 4 +:experimental: +:keywords: AsciiDoc +:source-highlighter: highlight.js +:icons: font +:imagesdir: ../../../assets/images + +For the following devices driver implementations are available in OpenEMS Edge. + +== Soltaro Battery Rack + +Implemented Natures:: +- Battery + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.battery.soltaro[Source Code icon:github[]] + +== FENECON Commercial 40 AC/DC/Hybrid + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss +- EssDcCharger + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.fenecon.commercial40[Source Code icon:github[]] + +== KACO blueplanet gridsave 50.0 TL3 + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.kaco.blueplanet.gridsave50[Source Code icon:github[]] + +== KACO blueplanet hybrid 10.0 TL3 + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss + +(proprietary) + +== Maschinenfabrik Rheinhausen (MR) Gridcon + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.mr.gridcon[Source Code icon:github[]] + +== REFU Battery Inverter + +Project specific implementation of a REFU inverter. This will not directly apply to all REFU battery inverters. + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss +- AsymmetricEss +- ManagedAsymmetricEss + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.refu[Source Code icon:github[]] + +== Sinexcel Battery Inverter + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.sinexcel[Source Code icon:github[]] + +== SMA SunnyIsland 6.0H + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss +- AsymmetricEss +- ManagedAsymmetricEss +- SinglePhaseEss +- ManagedSinglePhaseEss + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.sma[Source Code icon:github[]] + +== KEBA KeContact c-series Charging Station + +Implemented Natures:: +- Evcs (Electric Vehicle Charging Station) + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.evcs.keba.kecontact[Source Code icon:github[]] + +== FENECON DESS + +Applies to multiple similar products like the FENECON by BYD PRO Hybrid. + +Implemented Natures:: +- SymmetricEss +- AsymmetricEss +- EssDcCharger +- AsymmetricMeter (for Grid and AC-connected PV) +- SymmetricMeter (for Grid and AC-connected PV) + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.fenecon.dess[Source Code icon:github[]] + +== FENECON Mini 3-3 | 3-6 + +Implemented Natures:: +- SinglePhaseEss +- AsymmetricEss +- SymmetricEss +- SymmetricMeter (for Grid and PV) + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.fenecon.mini[Source Code icon:github[]] + +== FENECON Pro 9-12 + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss +- AsymmetricEss +- ManagedAsymmetricEss +- AsymmetricMeter (for PV) +- SymmetricMeter (for PV) + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.fenecon.pro[Source Code icon:github[]] + +== FENECON BYD Container + +Implemented Natures:: +- SymmetricEss +- ManagedSymmetricEss + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.byd.container[Source Code icon:github[]] + +== KMtronic Modbus Relay Board + +Implemented Natures:: +- DigitalOutput + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.io.kmtronic[Source Code icon:github[]] + +== WAGO Fieldbus Coupler 750-352 + +Implemented Natures:: +- DigitalOutput +- DigitalInput + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.io.wago[Source Code icon:github[]] + +== KOSTAL PIKO + +Implemented Natures:: +- SymmetricEss +- SymmetricMeter (for Grid meter) +- EssDcCharger (for PV) + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.kostal.piko[Source Code icon:github[]] + +== Artemes AM-2 + +Implemented Natures:: +- SymmetricMeter +- AsymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.artemes.am2[Source Code icon:github[]] + +== B-Control EM300 Meter + +Implemented Natures:: +- SymmetricMeter +- AsymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.bcontrol.em300[Source Code icon:github[]] + +== Carlo Gavazzi EM300 Meter + +Implemented Natures:: +- SymmetricMeter +- AsymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.carlo.gavazzi.em300[Source Code icon:github[]] + +== Janitza UMG 96RM-E Meter + +Implemented Natures:: +- SymmetricMeter +- AsymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.janitza.umg96rme[Source Code icon:github[]] + +== Microcare SDM 630 Meter + +This implementation is functionally compatible with a number of energy meters with the name "SDM 630". + +Implemented Natures:: +- SymmetricMeter +- AsymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.microcare.sdm630[Source Code icon:github[]] + +== SOCOMEC Diris A10 | A14 | B30 | E24 Meter + +Implemented Natures:: +- SymmetricMeter +- AsymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.socomec[Source Code icon:github[]] + +== Weidmueller 525 Meter + +Implemented Natures:: +- SymmetricMeter +- AsymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.weidmueller[Source Code icon:github[]] + +== Virtual Symmetric Add + +Implemented Natures:: +- SymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.virtual[Source Code icon:github[]] + +== Solar-Log + +Implemented Natures:: +- SymmetricPvInverter +- SymmetricMeter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.pvinverter.solarlog[Source Code icon:github[]] + + + diff --git a/doc/modules/ROOT/pages/edge/apps/schedulers.adoc b/doc/modules/ROOT/pages/edge/apps/schedulers.adoc new file mode 100644 index 00000000000..648b557c1e6 --- /dev/null +++ b/doc/modules/ROOT/pages/edge/apps/schedulers.adoc @@ -0,0 +1,24 @@ += Implemented Schedulers +:sectnums: +:sectnumlevels: 4 +:toc: +:toclevels: 4 +:experimental: +:keywords: AsciiDoc +:source-highlighter: highlight.js +:icons: font +:imagesdir: ../../../assets/images + +The following Schedulers are available in OpenEMS Edge. + +== All-Alphabetically + +Takes an ordered list of Component IDs. All remaining Controllers are afterwards ordered alphabetically by their ID. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.scheduler.allalphabetically[Source Code icon:github[]] + +== Fixed Order + +Takes a list of Component IDs and returns the Controllers statically sorted by this order. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.scheduler.fixedorder[Source Code icon:github[]] diff --git a/doc/modules/ROOT/pages/edge/apps/timedata.adoc b/doc/modules/ROOT/pages/edge/apps/timedata.adoc new file mode 100644 index 00000000000..069ca6bfd9f --- /dev/null +++ b/doc/modules/ROOT/pages/edge/apps/timedata.adoc @@ -0,0 +1,18 @@ += Timedata services +:sectnums: +:sectnumlevels: 4 +:toc: +:toclevels: 4 +:experimental: +:keywords: AsciiDoc +:source-highlighter: highlight.js +:icons: font +:imagesdir: ../../../assets/images + +The following timedata services are available for OpenEMS Edge. + +== InfluxDB + +Persists all data of OpenEMS Edge Channels to an InfluxDB timeseries database. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.timedata.influxdb[Source Code icon:github[]] diff --git a/doc/modules/ROOT/pages/edge/apps/utils.adoc b/doc/modules/ROOT/pages/edge/apps/utils.adoc new file mode 100644 index 00000000000..21d6a5743ae --- /dev/null +++ b/doc/modules/ROOT/pages/edge/apps/utils.adoc @@ -0,0 +1,18 @@ += Helpful utilities +:sectnums: +:sectnumlevels: 4 +:toc: +:toclevels: 4 +:experimental: +:keywords: AsciiDoc +:source-highlighter: highlight.js +:icons: font +:imagesdir: ../../../assets/images + +The following generic utilities are available in OpenEMS Edge. + +== ESS Cluster + +Combines multiple energy storage systems (ESS) to one common ESS. This way every Controller can easily work with multiple ESS in parallel. Distribution of power requests to each ESS is controlled via the https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power[Power-Class icon:github[]]. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.ess.cluster[Source Code icon:github[]] \ No newline at end of file diff --git a/doc/modules/ROOT/pages/edge/implement.adoc b/doc/modules/ROOT/pages/edge/implement.adoc index 0f09909ee74..194ddd1e7e6 100644 --- a/doc/modules/ROOT/pages/edge/implement.adoc +++ b/doc/modules/ROOT/pages/edge/implement.adoc @@ -160,35 +160,42 @@ import io.openems.edge.meter.api.MeterType; name = "Meter Simulated", // description = "Implements the simulated meter.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter0"; // <2> - boolean enabled() default true; // <3> + @AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID") + String alias() default ""; // <3> + + @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") + boolean enabled() default true; // <4> - @AttributeDefinition(name = "Meter-Type", description = "Grid, Production (=default), Consumption") // <4> - MeterType type() default MeterType.PRODUCTION; // <5> + @AttributeDefinition(name = "Meter-Type", description = "Grid, Production (=default), Consumption") // <5> + MeterType type() default MeterType.PRODUCTION; // <6> @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.") - String modbus_id(); // <6> + String modbus_id(); // <7> @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") - int modbusUnitId(); // <7> + int modbusUnitId(); // <8> @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") - String Modbus_target() default ""; // <8> + String Modbus_target() default ""; // <9> - String webconsole_configurationFactory_nameHint() default "Meter Simulated [{id}]"; // <9> + String webconsole_configurationFactory_nameHint() default "Meter Simulated [{id}]"; // <10> } ---- <1> The *@ObjectClassDefinition* annotation defines this file as a Meta Type Resource for OSGi configuration admin. Use it to set a _name_ and _description_ for this OpenEMS Component. // TODO add screenshot that shows how the strings are used in Apache <2> The *id* configuration parameter sets the OpenEMS Component-ID (see xref:coreconcepts.adoc[Channel Adress]). _Note_: A *default* ID 'meter0' is defined. It is good practice to define such an ID here, as it simplifies configuration in the UI. -<3> The *enabled* parameter provides a _soft_ way of deactivating an OpenEMS Component programmatically. -<4> The *@AttributeDefinition* annotation provides meta information about a configuration parameter like _name_ and _description_. -<5> The 'Meter' nature requires definition of a MeterType that defines the purpose of the Meter. We will let the user define this type by a configuration parameter. -<6> The 'Modbus-ID' parameter creates the link to a Modbus-Service via its OpenEMS Component-ID. At runtime the user will typically set this configuration parameter to something like 'modbus0'. -<7> The Modbus service implementation requires us to provide the Modbus _Unit-ID_ (also commonly called _Device-ID_ or _Slave-ID_) of the Modbus slave device. This is the ID that is configured at the simulated meter. -<8> The *Modbus_target* will be automatically set by OpenEMS framework and does usually not need to be configured by the user. _Note_: Linking other OpenEMS Components is implemented using OSGi References. The OpenEMS Edge framework therefor sets the 'target' property of a reference to filter the matched services. -<9> The *webconsole_configurationFactory_nameHint* parameter sets a custom name for Apache Felix Web Console, helping the user to find the correct bundle. +<3> The *alias* configuration parameter sets the human-readable name of this OpenEMS Component. If no alias is configured, the Component-ID is used instead. +<4> The *enabled* parameter provides a _soft_ way of deactivating an OpenEMS Component programmatically. +<5> The *@AttributeDefinition* annotation provides meta information about a configuration parameter like _name_ and _description_. +<6> The 'Meter' nature requires definition of a MeterType that defines the purpose of the Meter. We will let the user define this type by a configuration parameter. +<7> The 'Modbus-ID' parameter creates the link to a Modbus-Service via its OpenEMS Component-ID. At runtime the user will typically set this configuration parameter to something like 'modbus0'. +<8> The Modbus service implementation requires us to provide the Modbus _Unit-ID_ (also commonly called _Device-ID_ or _Slave-ID_) of the Modbus slave device. This is the ID that is configured at the simulated meter. +<9> The *Modbus_target* will be automatically set by OpenEMS framework and does usually not need to be configured by the user. _Note_: Linking other OpenEMS Components is implemented using OSGi References. The OpenEMS Edge framework therefor sets the 'target' property of a reference to filter the matched services. +<10> The *webconsole_configurationFactory_nameHint* parameter sets a custom name for Apache Felix Web Console, helping the user to find the correct bundle. === Implement the OpenEMS Component @@ -255,7 +262,7 @@ public class MeterSimulated extends AbstractOpenemsModbusComponent // <6> void activate(ComponentContext context, Config config) { // <11> this.meterType = config.type(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } diff --git a/edge/src/io/openems/impl/controller/acisland/AcIsland.java b/edge/src/io/openems/impl/controller/acisland/AcIsland.java deleted file mode 100644 index cb6972e8fb9..00000000000 --- a/edge/src/io/openems/impl/controller/acisland/AcIsland.java +++ /dev/null @@ -1,304 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016, 2017 FENECON GmbH and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Contributors: - * FENECON GmbH - initial API and implementation and initial documentation - *******************************************************************************/ -package io.openems.impl.controller.acisland; - -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.channel.thingstate.ThingStateChannels; -import io.openems.api.controller.Controller; -import io.openems.api.device.nature.ess.EssNature; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.ConfigException; -import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.WriteChannelException; -import io.openems.core.ThingRepository; - -@ThingInfo(title = "Use AC-PV in offgrid situation") -public class AcIsland extends Controller { - - private ThingRepository repo = ThingRepository.getInstance(); - private Optional> onGridOutputChannel; - private Optional> offGridOutputChannel; - private State currentState = State.UNKNOWN; - private boolean isProducerDisconnected = false; - private long timeProducerDisconnected; - private ThingStateChannels thingState = new ThingStateChannels(this); - - private enum State { - OFFGRID, ONGRID, SWITCHTOOFFGRID, SWITCHTOONGRID, UNKNOWN - } - - // ConfigChannel - - @ChannelInfo(title = "soc to disconnect the producer if the system is Off-Grid.", type = Long.class) - public ConfigChannel maxSoc = new ConfigChannel("maxSoc", this).defaultValue(85L); - @ChannelInfo(title = "soc to connect the producer if the system is Off-Grid.", type = Long.class) - public ConfigChannel minSoc = new ConfigChannel("minSoc", this).defaultValue(70L); - - @ChannelInfo(title = "The ess where the grid state should be read from.", type = Ess.class) - public ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "time to wait before switch output on.", type = Long.class) - public ConfigChannel switchDelay = new ConfigChannel("switchDelay", this).defaultValue(10000L); - - @ChannelInfo(title = "Invert-On-Grid-Output", description = "True if the digital output for the On-Grid connector should be inverted.", type = Boolean.class) - public ConfigChannel invertOnGridOutput = new ConfigChannel("invertOnGridOutput", this).defaultValue(false); - - @ChannelInfo(title = "Invert-Off-Grid-Output", description = "True if the digital output for the Off-Grid connector should be inverted.", type = Boolean.class) - public ConfigChannel invertOffGridOutput = new ConfigChannel("invertOffGridOutput", this).defaultValue(false); - - @SuppressWarnings("unchecked") - @ChannelInfo(title = "the address of the Digital Output where the on grid connection of producer is connected to.", type = String.class) - public ConfigChannel onGridOutputChannelAddress = new ConfigChannel("onGridOutputChannelAddress", - this).addChangeListener((channel, newValue, oldValue) -> { - Optional channelAddress = (Optional) newValue; - if (channelAddress.isPresent()) { - Optional ch = repo.getChannelByAddress(channelAddress.get()); - if (ch.isPresent()) { - this.onGridOutputChannel = Optional.of( // - ((WriteChannel) ch.get()).required()); - // TODO should not be necessary to set outputChannel as required - } else { - log.error("Channel " + channelAddress.get() + " not found"); - } - } else { - log.error("'onGridOutputChannelAddress' is not configured!"); - } - }); - - @SuppressWarnings("unchecked") - @ChannelInfo(title = "the address of the Digital Output where the off grid connection of producer is connected to.", type = String.class) - public ConfigChannel offGridOutputChannelAddress = new ConfigChannel("offGridOutputChannelAddress", - this).addChangeListener((channel, newValue, oldValue) -> { - Optional channelAddress = (Optional) newValue; - if (channelAddress.isPresent()) { - Optional ch = repo.getChannelByAddress(channelAddress.get()); - if (ch.isPresent()) { - this.offGridOutputChannel = Optional.of( // - ((WriteChannel) ch.get()).required()); - // TODO should not be necessary to set outputChannel as required - } else { - log.error("Channel " + channelAddress.get() + " not found"); - } - } else { - log.error("'offGridOutputChannelAddress' is not configured!"); - } - }); - - public AcIsland() { - super(); - } - - public AcIsland(String thingId) { - super(thingId); - } - - @Override - public void run() { - // Get all required values - or abort with error - Ess ess; - long maxSoc; - long minSoc; - long soc; - String gridMode; - boolean isProducerOff; - boolean isProducerOffGrid; - boolean isProducerOnGrid; - long switchDelay; - try { - ess = this.ess.value(); - soc = ess.soc.value(); - gridMode = ess.gridMode.labelOptional().get(); - maxSoc = this.maxSoc.value(); - minSoc = this.minSoc.value(); - isProducerOff = this.isProducerOff(); - isProducerOffGrid = this.isProducerOffGrid(); - isProducerOnGrid = this.isProducerOnGrid(); - switchDelay = this.switchDelay.value(); - } catch (InvalidValueException | ConfigException e) { - log.error(e.getMessage()); - return; - } - - try { - switch (currentState) { - case OFFGRID: - if (isProducerOffGrid || isProducerOff) { - //Check if offGrid otherwise go to switchOnGrid - if (gridMode.equals(EssNature.ON_GRID)) { - currentState = State.SWITCHTOONGRID; - } else { - //Check if PV is allowed to run - if (soc >= maxSoc) { - disconnectOffGrid(); - } else if (soc <= minSoc) { - connectOffGrid(); - } - } - } else { - currentState = State.SWITCHTOOFFGRID; - } - break; - case ONGRID: { - //Check if onGrid. If es goes offGrid go to switchOffGrid - if (isProducerOnGrid) { - if (gridMode.equals(EssNature.OFF_GRID)) { - currentState = State.SWITCHTOOFFGRID; - } - } else { - currentState = State.SWITCHTOONGRID; - } - } - break; - case SWITCHTOOFFGRID: - //Disconnect PV, wait switchDelay and go to OffGrid - if (isProducerOff) { - if (!isProducerDisconnected) { - isProducerDisconnected = true; - timeProducerDisconnected = System.currentTimeMillis(); - } - if (timeProducerDisconnected + switchDelay <= System.currentTimeMillis() - && isProducerDisconnected) { - currentState = State.OFFGRID; - } - } else { - isProducerDisconnected = false; - disconnectOnGrid(); - disconnectOffGrid(); - } - break; - case SWITCHTOONGRID: - //Disconnect PV, wait switchDelay and connect PV onGrid - if (isProducerOnGrid) { - currentState = State.ONGRID; - isProducerDisconnected = false; - } else { - if (isProducerOff) { - if (!isProducerDisconnected) { - isProducerDisconnected = true; - timeProducerDisconnected = System.currentTimeMillis(); - } - if (timeProducerDisconnected + switchDelay <= System.currentTimeMillis() - && isProducerDisconnected) { - connectOnGrid(); - } - } else { - isProducerDisconnected = false; - disconnectOnGrid(); - disconnectOffGrid(); - } - } - break; - default: { - if (gridMode.equals(EssNature.ON_GRID)) { - currentState = State.SWITCHTOONGRID; - } else if (ess.gridMode.labelOptional().equals(Optional.of(EssNature.OFF_GRID))) { - currentState = State.SWITCHTOOFFGRID; - } - } - break; - - } - } catch (WriteChannelException | ConfigException e) { - log.error("Failed to switch Output!", e); - } - } - - private boolean isProducerOff() throws InvalidValueException, ConfigException { - return isOnGridOn() == false && isOffGridOn() == false; - } - - private boolean isProducerOffGrid() throws InvalidValueException, ConfigException { - return isOnGridOn() == false && isOffGridOn() == true; - } - - private boolean isProducerOnGrid() throws InvalidValueException, ConfigException { - return isOnGridOn() == true && isOffGridOn() == false; - } - - private WriteChannel getOnGridOutputChannel() throws ConfigException { - if (this.onGridOutputChannel.isPresent()) { - return this.onGridOutputChannel.get(); - } else { - throw new ConfigException("onGridOutputChannel is not available."); - } - } - - private WriteChannel getOffGridOutputChannel() throws ConfigException { - if (this.offGridOutputChannel.isPresent()) { - return this.offGridOutputChannel.get(); - } else { - throw new ConfigException("offGridOutputChannel is not available."); - } - } - - private void connectOnGrid() throws ConfigException, WriteChannelException { - WriteChannel outputChannel = getOnGridOutputChannel(); - boolean invertOutput = invertOnGridOutput.valueOptional().orElse(false); - switchOutput(outputChannel,true, invertOutput); - } - - private void disconnectOnGrid() throws ConfigException, WriteChannelException { - WriteChannel outputChannel = getOnGridOutputChannel(); - boolean invertOutput = invertOnGridOutput.valueOptional().orElse(false); - switchOutput(outputChannel,false, invertOutput); - } - - private boolean isOnGridOn() throws ConfigException, InvalidValueException { - WriteChannel outputChannel = getOnGridOutputChannel(); - boolean invertOutput = invertOnGridOutput.valueOptional().orElse(false); - return outputChannel.value() ^ invertOutput; - } - - private void connectOffGrid() throws ConfigException, WriteChannelException { - WriteChannel outputChannel = getOffGridOutputChannel(); - boolean invertOutput = invertOffGridOutput.valueOptional().orElse(false); - switchOutput(outputChannel,true, invertOutput); - } - - private void disconnectOffGrid() throws ConfigException, WriteChannelException { - WriteChannel outputChannel = getOffGridOutputChannel(); - boolean invertOutput = invertOffGridOutput.valueOptional().orElse(false); - switchOutput(outputChannel,false, invertOutput); - } - - private boolean isOffGridOn() throws ConfigException, InvalidValueException { - WriteChannel outputChannel = getOffGridOutputChannel(); - boolean invertOutput = invertOffGridOutput.valueOptional().orElse(false); - return outputChannel.value() ^ invertOutput; - } - - private void switchOutput(WriteChannel outputChannel, boolean on,boolean invertOutput) throws WriteChannelException { - Optional currentValueOpt = outputChannel.valueOptional(); - if (!currentValueOpt.isPresent() || currentValueOpt.get() != (on ^ invertOutput)) { - outputChannel.pushWrite(on ^ invertOutput); - } - } - - @Override - public ThingStateChannels getStateChannel() { - return this.thingState; - } -} diff --git a/edge/src/io/openems/impl/controller/acisland/Ess.java b/edge/src/io/openems/impl/controller/acisland/Ess.java deleted file mode 100644 index 3a36c0e38b2..00000000000 --- a/edge/src/io/openems/impl/controller/acisland/Ess.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016, 2017 FENECON GmbH and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Contributors: - * FENECON GmbH - initial API and implementation and initial documentation - *******************************************************************************/ -package io.openems.impl.controller.acisland; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.EssNature; - -@IsThingMap(type = EssNature.class) -public class Ess extends ThingMap { - - public ReadChannel gridMode; - public ReadChannel soc; - - public Ess(EssNature thing) { - super(thing); - this.gridMode = thing.gridMode().required(); - this.soc = thing.soc().required(); - } - -} diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index 88c744a8b57..e265d126f9c 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -19,6 +19,7 @@ bnd.identity;id='org.ops4j.pax.logging.pax-logging-api',\ bnd.identity;id='io.openems.backend.application',\ bnd.identity;id='io.openems.backend.timedata.influx',\ + bnd.identity;id='io.openems.backend.b2brest',\ bnd.identity;id='io.openems.backend.b2bwebsocket',\ bnd.identity;id='io.openems.backend.edgewebsocket.impl',\ bnd.identity;id='io.openems.backend.metadata.dummy',\ @@ -64,4 +65,5 @@ org.osgi.service.event;version='[1.3.1,1.3.2)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ org.jsr-305;version='[3.0.2,3.0.3)',\ - com.google.guava;version='[27.1.0,27.1.1)' \ No newline at end of file + com.google.guava;version='[27.1.0,27.1.1)',\ + io.openems.backend.b2brest;version=snapshot \ No newline at end of file diff --git a/io.openems.backend.b2brest/.classpath b/io.openems.backend.b2brest/.classpath new file mode 100644 index 00000000000..3ebd512b99a --- /dev/null +++ b/io.openems.backend.b2brest/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/io.openems.backend.b2brest/.gitignore b/io.openems.backend.b2brest/.gitignore new file mode 100644 index 00000000000..c2b941a96de --- /dev/null +++ b/io.openems.backend.b2brest/.gitignore @@ -0,0 +1,2 @@ +/bin_test/ +/generated/ diff --git a/io.openems.edge.meter.simulated/.project b/io.openems.backend.b2brest/.project similarity index 91% rename from io.openems.edge.meter.simulated/.project rename to io.openems.backend.b2brest/.project index ff37d9a630a..cfd20a40fc3 100644 --- a/io.openems.edge.meter.simulated/.project +++ b/io.openems.backend.b2brest/.project @@ -1,6 +1,6 @@ - io.openems.edge.meter.simulated + io.openems.backend.b2brest diff --git a/io.openems.backend.b2brest/bnd.bnd b/io.openems.backend.b2brest/bnd.bnd new file mode 100644 index 00000000000..55aa6a5b0d7 --- /dev/null +++ b/io.openems.backend.b2brest/bnd.bnd @@ -0,0 +1,28 @@ +Bundle-Name: OpenEMS Backend-to-Backend REST-Api +Bundle-Vendor: FENECON GmbH +Bundle-License: https://opensource.org/licenses/EPL-2.0 +Bundle-Version: 1.0.0.${tstamp} + +Private-Package: \ + io.openems.backend.b2brest + +-includeresource: {readme.md} + +-buildpath: ${buildpath},\ + io.openems.backend.common;version=latest,\ + io.openems.backend.edgewebsocket.api;version=latest,\ + io.openems.backend.metadata.api;version=latest,\ + io.openems.backend.timedata.api;version=latest,\ + io.openems.common;version=latest,\ + io.openems.wrapper.websocket;version=latest,\ + com.google.gson;version=2.8,\ + com.google.guava,\ + slf4j.api,\ + org.eclipse.jetty.server,\ + org.apache.felix.http.jetty,\ + javax.servlet-api + +-testpath: ${testpath} + +javac.source: 1.8 +javac.target: 1.8 \ No newline at end of file diff --git a/io.openems.backend.b2brest/readme.md b/io.openems.backend.b2brest/readme.md new file mode 100644 index 00000000000..ca928165e6f --- /dev/null +++ b/io.openems.backend.b2brest/readme.md @@ -0,0 +1,30 @@ +# Backend-to-Backend REST-Api + +## Endpoint '/jsonrpc' + +Properties 'id' and 'jsonrpc' can be omitted, as they are not required for HTTP POST calls. + +### getEdgesStatus + +``` +{ + "method": "getEdgesStatus", + "params": {} +} +``` + +### getEdgesChannelsValues + +``` +{ + "method":"getEdgesChannelsValues", + "params": { + "ids": [ + "edge0" + ], + "channels": [ + "_sum/State" + ] + } +} +``` diff --git a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/B2bRest.java b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/B2bRest.java new file mode 100644 index 00000000000..d2b28608039 --- /dev/null +++ b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/B2bRest.java @@ -0,0 +1,98 @@ +package io.openems.backend.b2brest; + +import org.eclipse.jetty.server.Server; +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.metatype.annotations.Designate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.backend.common.component.AbstractOpenemsBackendComponent; +import io.openems.backend.edgewebsocket.api.EdgeWebsocket; +import io.openems.backend.metadata.api.Metadata; +import io.openems.backend.timedata.api.Timedata; +import io.openems.common.exceptions.OpenemsException; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "Backend2Backend.Rest", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +public class B2bRest extends AbstractOpenemsBackendComponent { + + public static final int DEFAULT_PORT = 8075; + + private final Logger log = LoggerFactory.getLogger(B2bRest.class); + + private Server server = null; + + @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC) + protected volatile EdgeWebsocket edgeWebsocket; + + @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC) + protected volatile Metadata metadata; + + @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC) + protected volatile Timedata timeData; + + public B2bRest() { + super("Backend2Backend.Rest"); + } + + @Activate + void activate(Config config) throws OpenemsException { + this.startServer(config.port()); + } + + @Deactivate + void deactivate() { + this.stopServer(); + } + + /** + * Create and start new server. + * + * @param port the port + * @throws OpenemsException on error + */ + private synchronized void startServer(int port) throws OpenemsException { + try { + this.server = new Server(port); + this.server.setHandler(new RestHandler(this)); + this.server.start(); + this.logInfo(this.log, "Backend2Backend.Rest started on port [" + port + "]."); + } catch (Exception e) { + throw new OpenemsException("Backend2Backend.Rest failed on port [" + port + "].", e); + } + } + + /** + * Stop existing server. + */ + private synchronized void stopServer() { + if (this.server != null) { + try { + this.server.stop(); + } catch (Exception e) { + this.logWarn(this.log, "Backend2Backend.Rest failed to stop: " + e.getMessage()); + } + } + } + + @Override + protected void logInfo(Logger log, String message) { + super.logInfo(log, message); + } + + @Override + protected void logWarn(Logger log, String message) { + super.logWarn(log, message); + } + +} diff --git a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/Config.java b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/Config.java new file mode 100644 index 00000000000..2f4aeca9fa0 --- /dev/null +++ b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/Config.java @@ -0,0 +1,16 @@ +package io.openems.backend.b2brest; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition(// + name = "Backend2Backend.Rest", // + description = "Provides a REST-Api server for backend-to-backend communication.") +@interface Config { + + @AttributeDefinition(name = "Port", description = "The port of the REST server.") + int port() default B2bRest.DEFAULT_PORT; + + String webconsole_configurationFactory_nameHint() default "Backend2Backend Rest"; + +} \ No newline at end of file diff --git a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java new file mode 100644 index 00000000000..64c68ae77bb --- /dev/null +++ b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java @@ -0,0 +1,328 @@ +package io.openems.backend.b2brest; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.StringTokenizer; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import io.openems.backend.common.jsonrpc.request.GetEdgesChannelsValuesRequest; +import io.openems.backend.common.jsonrpc.request.GetEdgesStatusRequest; +import io.openems.backend.common.jsonrpc.response.GetEdgesChannelsValuesResponse; +import io.openems.backend.common.jsonrpc.response.GetEdgesStatusResponse; +import io.openems.backend.common.jsonrpc.response.GetEdgesStatusResponse.EdgeInfo; +import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.metadata.api.Edge; +import io.openems.common.exceptions.OpenemsError; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.base.JsonrpcMessage; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; +import io.openems.common.jsonrpc.request.SetGridConnScheduleRequest; +import io.openems.common.session.Role; +import io.openems.common.session.User; +import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; + +public class RestHandler extends AbstractHandler { + + private final Logger log = LoggerFactory.getLogger(RestHandler.class); + + private final B2bRest parent; + + public RestHandler(B2bRest parent) { + this.parent = parent; + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + try { + BackendUser user = this.authenticate(request); + + List targets = Arrays.asList(// + target.substring(1) // remove leading '/' + .split("/")); + + if (targets.isEmpty()) { + throw new OpenemsException("Missing arguments to handle request"); + } + + String thisTarget = targets.get(0); + switch (thisTarget) { + case "jsonrpc": + this.handleJsonRpc(user, baseRequest, request, response); + break; + } + } catch (OpenemsNamedException e) { + throw new IOException(e.getMessage()); + } + } + + /** + * Authenticate a user. + * + * @param request the HttpServletRequest + * @return the User + * @throws OpenemsNamedException on error + */ + private BackendUser authenticate(HttpServletRequest request) throws OpenemsNamedException { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null) { + StringTokenizer st = new StringTokenizer(authHeader); + if (st.hasMoreTokens()) { + String basic = st.nextToken(); + if (basic.equalsIgnoreCase("Basic")) { + String credentials; + try { + credentials = new String(Base64.getDecoder().decode(st.nextToken()), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw OpenemsError.COMMON_AUTHENTICATION_FAILED.exception(); + } + int p = credentials.indexOf(":"); + if (p != -1) { + String username = credentials.substring(0, p).trim(); + String password = credentials.substring(p + 1).trim(); + // authenticate using username & password + return this.parent.metadata.authenticate(username, password); + } + } + } + } + throw OpenemsError.COMMON_AUTHENTICATION_FAILED.exception(); + } + + private void sendOkResponse(Request baseRequest, HttpServletResponse response, JsonObject data) + throws OpenemsException { + try { + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + baseRequest.setHandled(true); + response.getWriter().write(data.toString()); + } catch (IOException e) { + throw new OpenemsException("Unable to send Ok-Response: " + e.getMessage()); + } + } + + /** + * Parses a Request to JSON. + * + * @param baseRequest the Request + * @return + * @throws OpenemsException on error + */ + private static JsonObject parseJson(Request baseRequest) throws OpenemsException { + JsonParser parser = new JsonParser(); + try { + return parser.parse(new BufferedReader(new InputStreamReader(baseRequest.getInputStream())).lines() + .collect(Collectors.joining("\n"))).getAsJsonObject(); + } catch (Exception e) { + throw new OpenemsException("Unable to parse: " + e.getMessage()); + } + } + + /** + * Handles an http request to 'jsonrpc' endpoint. + * + * @param user the User + * @param edgeRpcRequest the EdgeRpcRequest + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + * @throws ExecutionException + * @throws InterruptedException + */ + private void handleJsonRpc(BackendUser user, Request baseRequest, HttpServletRequest httpRequest, + HttpServletResponse httpResponse) throws OpenemsNamedException { + // call handler methods + if (!httpRequest.getMethod().equals("POST")) { + throw new OpenemsException( + "Method [" + httpRequest.getMethod() + "] is not supported for JSON-RPC endpoint"); + } + + // parse json and add "jsonrpc" and "id" properties if missing + JsonObject json = RestHandler.parseJson(baseRequest); + if (!json.has("jsonrpc")) { + json.addProperty("jsonrpc", "2.0"); + } + if (!json.has("id")) { + json.addProperty("id", UUID.randomUUID().toString()); + } + if (json.has("params")) { + JsonObject params = JsonUtils.getAsJsonObject(json, "params"); + if (params.has("payload")) { + JsonObject payload = JsonUtils.getAsJsonObject(params, "payload"); + if (!payload.has("jsonrpc")) { + payload.addProperty("jsonrpc", "2.0"); + } + if (!payload.has("id")) { + payload.addProperty("id", UUID.randomUUID().toString()); + } + params.add("payload", payload); + } + json.add("params", params); + } + + // parse JSON-RPC Request + JsonrpcMessage message = JsonrpcMessage.from(json); + if (!(message instanceof JsonrpcRequest)) { + throw new OpenemsException("Only JSON-RPC Request is supported here."); + } + JsonrpcRequest request = (JsonrpcRequest) message; + + // handle the request + CompletableFuture responseFuture = this.handleJsonRpcRequest(user, request); + + // wait for response + JsonrpcResponseSuccess response; + try { + response = responseFuture.get(); + } catch (InterruptedException | ExecutionException e) { + throw new OpenemsException("Unable to get Response: " + e.getMessage()); + } + + // send response + this.sendOkResponse(baseRequest, httpResponse, response.toJsonObject()); + } + + /** + * Handles an JSON-RPC Request. + * + * @param user the User + * @param edgeRpcRequest the EdgeRpcRequest + * @return the JSON-RPC Success Response Future + * @throws OpenemsException on error + */ + private CompletableFuture handleJsonRpcRequest(BackendUser user, + JsonrpcRequest request) throws OpenemsException, OpenemsNamedException { + switch (request.getMethod()) { + + case GetEdgesStatusRequest.METHOD: + return this.handleGetStatusOfEdgesRequest(user, request.getId(), GetEdgesStatusRequest.from(request)); + + case GetEdgesChannelsValuesRequest.METHOD: + return this.handleGetChannelsValuesRequest(user, request.getId(), + GetEdgesChannelsValuesRequest.from(request)); + + case SetGridConnScheduleRequest.METHOD: + return this.handleSetGridConnScheduleRequest(user, request.getId(), + SetGridConnScheduleRequest.from(request)); + + default: + this.parent.logWarn(this.log, "Unhandled Request: " + request); + throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); + } + } + + /** + * Handles a GetStatusOfEdgesRequest. + * + * @param user the User + * @param messageId the JSON-RPC Message-ID + * @param request the GetStatusOfEdgesRequest + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleGetStatusOfEdgesRequest(BackendUser user, UUID messageId, + GetEdgesStatusRequest request) throws OpenemsNamedException { + Map result = new HashMap<>(); + for (Entry entry : user.getEdgeRoles().entrySet()) { + String edgeId = entry.getKey(); + + // assure read permissions of this User for this Edge. + if (!user.edgeRoleIsAtLeast(edgeId, Role.GUEST)) { + continue; + } + + Optional edgeOpt = this.parent.metadata.getEdge(edgeId); + if (edgeOpt.isPresent()) { + Edge edge = edgeOpt.get(); + EdgeInfo info = new EdgeInfo(edge.isOnline()); + result.put(edge.getId(), info); + } + } + return CompletableFuture.completedFuture(new GetEdgesStatusResponse(messageId, result)); + } + + /** + * Handles a GetChannelsValuesRequest. + * + * @param user the User + * @param messageId the JSON-RPC Message-ID + * @param request the GetChannelsValuesRequest + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleGetChannelsValuesRequest(BackendUser user, + UUID messageId, GetEdgesChannelsValuesRequest request) throws OpenemsNamedException { + GetEdgesChannelsValuesResponse response = new GetEdgesChannelsValuesResponse(messageId); + for (String edgeId : request.getEdgeIds()) { + // assure read permissions of this User for this Edge. + if (!user.edgeRoleIsAtLeast(edgeId, Role.GUEST)) { + continue; + } + + for (ChannelAddress channel : request.getChannels()) { + Optional value = this.parent.timeData.getChannelValue(edgeId, channel); + response.addValue(edgeId, channel, value.orElse(JsonNull.INSTANCE)); + } + } + return CompletableFuture.completedFuture(response); + } + + /** + * Handles a SetGridConnScheduleRequest. + * + * @param backendUser the User + * @param messageId the JSON-RPC Message-ID + * @param setGridConnScheduleRequest the SetGridConnScheduleRequest + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleSetGridConnScheduleRequest(BackendUser backendUser, + UUID messageId, SetGridConnScheduleRequest setGridConnScheduleRequest) throws OpenemsNamedException { + String edgeId = setGridConnScheduleRequest.getEdgeId(); + User user = backendUser.getAsCommonUser(edgeId); + user.assertRoleIsAtLeast(SetGridConnScheduleRequest.METHOD, Role.ADMIN); + + // wrap original request inside ComponentJsonApiRequest + String componentId = "ctrlBalancingSchedule0"; // TODO find dynamic Component-ID of BalancingScheduleController + ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, setGridConnScheduleRequest); + + CompletableFuture resultFuture = this.parent.edgeWebsocket.send(edgeId, user, request); + + // Wrap reply in GenericJsonrpcResponseSuccess + CompletableFuture result = new CompletableFuture(); + resultFuture.thenAccept(r -> { + result.complete(new GenericJsonrpcResponseSuccess(messageId, r.toJsonObject())); + }); + return result; + } +} diff --git a/io.openems.edge.ess.sinexcel/bin_test/.gitignore b/io.openems.backend.b2brest/test/.gitignore similarity index 100% rename from io.openems.edge.ess.sinexcel/bin_test/.gitignore rename to io.openems.backend.b2brest/test/.gitignore diff --git a/io.openems.backend.b2bwebsocket/bnd.bnd b/io.openems.backend.b2bwebsocket/bnd.bnd index 464800e7519..f741b7d22a7 100644 --- a/io.openems.backend.b2bwebsocket/bnd.bnd +++ b/io.openems.backend.b2bwebsocket/bnd.bnd @@ -9,8 +9,7 @@ Export-Package: \ Private-Package: \ io.openems.backend.b2bwebsocket,\ io.openems.backend.b2bwebsocket.jsonrpc.notification,\ - io.openems.backend.b2bwebsocket.jsonrpc.request,\ - io.openems.backend.b2bwebsocket.jsonrpc.response + io.openems.backend.b2bwebsocket.jsonrpc.request -includeresource: {readme.md} @@ -21,7 +20,7 @@ Private-Package: \ io.openems.backend.timedata.api;version=latest,\ io.openems.common;version=latest,\ io.openems.wrapper.websocket;version=latest,\ - com.google.gson;version=2.8,\ + com.google.gson,\ com.google.guava,\ slf4j.api diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java index 73b48d42338..fce45b7118d 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java @@ -21,7 +21,7 @@ public OnClose(B2bWebsocket parent) { @Override public void run(WebSocket ws, int code, String reason, boolean remote) throws OpenemsException { WsData wsData = ws.getAttachment(); - Optional user = wsData.getUser(); + Optional user = wsData.getUserOpt(); if (user.isPresent()) { this.parent.logInfo(this.log, "User [" + user.get().getName() + "] closed connection"); } else { diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java index 01f5d46a32b..606f6dd63c2 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java @@ -6,6 +6,7 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import org.java_websocket.WebSocket; import org.slf4j.Logger; @@ -14,12 +15,12 @@ import com.google.gson.JsonElement; import com.google.gson.JsonNull; -import io.openems.backend.b2bwebsocket.jsonrpc.request.GetEdgesChannelsValuesRequest; -import io.openems.backend.b2bwebsocket.jsonrpc.request.GetEdgesStatusRequest; import io.openems.backend.b2bwebsocket.jsonrpc.request.SubscribeEdgesChannelsRequest; -import io.openems.backend.b2bwebsocket.jsonrpc.response.GetEdgesChannelsValuesResponse; -import io.openems.backend.b2bwebsocket.jsonrpc.response.GetEdgesStatusResponse; -import io.openems.backend.b2bwebsocket.jsonrpc.response.GetEdgesStatusResponse.EdgeInfo; +import io.openems.backend.common.jsonrpc.request.GetEdgesChannelsValuesRequest; +import io.openems.backend.common.jsonrpc.request.GetEdgesStatusRequest; +import io.openems.backend.common.jsonrpc.response.GetEdgesChannelsValuesResponse; +import io.openems.backend.common.jsonrpc.response.GetEdgesStatusResponse; +import io.openems.backend.common.jsonrpc.response.GetEdgesStatusResponse.EdgeInfo; import io.openems.backend.metadata.api.BackendUser; import io.openems.backend.metadata.api.Edge; import io.openems.common.exceptions.OpenemsError; @@ -47,7 +48,7 @@ public OnRequest(B2bWebsocket parent) { public CompletableFuture run(WebSocket ws, JsonrpcRequest request) throws OpenemsException, OpenemsNamedException { WsData wsData = ws.getAttachment(); - BackendUser user = wsData.assertUser(); + BackendUser user = wsData.getUserWithTimeout(5, TimeUnit.SECONDS); switch (request.getMethod()) { @@ -143,7 +144,6 @@ private CompletableFuture handleSubscribeEdgesCha for (String edgeId : request.getEdgeIds()) { // assure read permissions of this User for this Edge. user.assertEdgeRoleIsAtLeast(SubscribeEdgesChannelsRequest.METHOD, edgeId, Role.GUEST); - request.removeEdgeId(edgeId); } // activate SubscribedChannelsWorker diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java index 377cb592da7..093a8369e2e 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java @@ -130,7 +130,7 @@ public void dispose() { */ private EdgesCurrentDataNotification getCurrentDataNotification() throws OpenemsNamedException { EdgesCurrentDataNotification result = new EdgesCurrentDataNotification(); - BackendUser user = this.wsData.assertUser(); + BackendUser user = this.wsData.getUserWithTimeout(5, TimeUnit.SECONDS); for (String edgeId : this.edgeIds) { // assure read permissions of this User for this Edge. diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java index bc36dbdbacb..e91ef514388 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java @@ -1,6 +1,10 @@ package io.openems.backend.b2bwebsocket; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import io.openems.backend.metadata.api.BackendUser; import io.openems.common.exceptions.OpenemsError; @@ -9,7 +13,7 @@ public class WsData extends io.openems.common.websocket.WsData { private final SubscribedEdgesChannelsWorker worker; - private BackendUser user = null; + private CompletableFuture user = new CompletableFuture(); public WsData(B2bWebsocket parent) { this.worker = new SubscribedEdgesChannelsWorker(parent, this); @@ -21,26 +25,25 @@ public void dispose() { } public void setUser(BackendUser user) { - this.user = user; + this.user.complete(user); } - public Optional getUser() { - return Optional.ofNullable(user); + // TODO Use "Future" in all bundles to avoid authenticated failed errors + // if the websocket had not been fully opened before the first JSON-RPC Request + public CompletableFuture getUser() { + return this.user; } - /** - * Gets the authenticated User or throws an Exception if User is not - * authenticated. - * - * @return the User - * @throws OpenemsNamedException if User is not authenticated - */ - public BackendUser assertUser() throws OpenemsNamedException { - Optional userOpt = this.getUser(); - if (!userOpt.isPresent()) { - OpenemsError.COMMON_USER_NOT_AUTHENTICATED.exception(""); + public BackendUser getUserWithTimeout(long timeout, TimeUnit unit) throws OpenemsNamedException { + try { + return this.user.get(timeout, unit); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw OpenemsError.COMMON_USER_NOT_AUTHENTICATED.exception("UNKNOWN"); } - return userOpt.get(); + } + + public Optional getUserOpt() { + return Optional.ofNullable(this.user.getNow(null)); } /** diff --git a/io.openems.backend.b2bwebsocket/test/io/openems/backend/b2bwebsocket/B2bWebsocketTest.java b/io.openems.backend.b2bwebsocket/test/io/openems/backend/b2bwebsocket/B2bWebsocketTest.java index 3aa8d3c39a1..4ad0fc7a177 100644 --- a/io.openems.backend.b2bwebsocket/test/io/openems/backend/b2bwebsocket/B2bWebsocketTest.java +++ b/io.openems.backend.b2bwebsocket/test/io/openems/backend/b2bwebsocket/B2bWebsocketTest.java @@ -9,9 +9,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import io.openems.backend.b2bwebsocket.jsonrpc.request.GetEdgesChannelsValuesRequest; -import io.openems.backend.b2bwebsocket.jsonrpc.request.GetEdgesStatusRequest; import io.openems.backend.b2bwebsocket.jsonrpc.request.SubscribeEdgesChannelsRequest; +import io.openems.backend.common.jsonrpc.request.GetEdgesChannelsValuesRequest; +import io.openems.backend.common.jsonrpc.request.GetEdgesStatusRequest; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.SetGridConnScheduleRequest; @@ -26,9 +26,9 @@ */ public class B2bWebsocketTest { - private static final String URI = "ws://localhost:" + B2bWebsocket.DEFAULT_PORT; - private static final String USERNAME = "demo@fenecon.de"; - private static final String PASSWORD = "femsdemo"; + private static final String URI = "ws://localhost:8076"; + private static final String USERNAME = "user"; + private static final String PASSWORD = "password"; private static TestClient prepareTestClient() throws URISyntaxException, InterruptedException { Map httpHeaders = new HashMap<>(); @@ -40,7 +40,7 @@ private static TestClient prepareTestClient() throws URISyntaxException, Interru return client; } -// @Test + // @Test public void testGetEdgesStatusRequest() throws URISyntaxException, InterruptedException, ExecutionException, OpenemsNamedException { TestClient client = prepareTestClient(); @@ -61,7 +61,6 @@ public void testGetEdgesChannelsValuesRequest() throws URISyntaxException, Inter GetEdgesChannelsValuesRequest request = new GetEdgesChannelsValuesRequest(); request.addEdgeId("edge0"); - request.addEdgeId("edge5"); request.addChannel(new ChannelAddress("_sum", "EssSoc")); request.addChannel(new ChannelAddress("_sum", "ProductionActivePower")); try { @@ -83,7 +82,6 @@ public void testSubscribeEdgesChannelsRequest() SubscribeEdgesChannelsRequest request = new SubscribeEdgesChannelsRequest(0); request.addEdgeId("edge0"); - request.addEdgeId("edge5"); request.addChannel(new ChannelAddress("_sum", "EssSoc")); request.addChannel(new ChannelAddress("_sum", "ProductionActivePower")); try { @@ -97,14 +95,14 @@ public void testSubscribeEdgesChannelsRequest() client.stop(); } - // @Test +// @Test public void testSetGridConnSchedule() throws URISyntaxException, InterruptedException { TestClient client = prepareTestClient(); SetGridConnScheduleRequest request = new SetGridConnScheduleRequest("edge0"); long now = System.currentTimeMillis() / 1000; - request.addScheduleEntry(new GridConnSchedule(now, 60, -3000)); - request.addScheduleEntry(new GridConnSchedule(now + 60, 60, -5000)); + request.addScheduleEntry(new GridConnSchedule(now, 60, 0)); + // request.addScheduleEntry(new GridConnSchedule(now + 60, 60, -5000)); try { CompletableFuture responseFuture = client.sendRequest(request); System.out.println(responseFuture.get().toString()); diff --git a/io.openems.backend.common/bnd.bnd b/io.openems.backend.common/bnd.bnd index a29f97fb7ea..f388121cce7 100644 --- a/io.openems.backend.common/bnd.bnd +++ b/io.openems.backend.common/bnd.bnd @@ -4,12 +4,16 @@ Bundle-License: https://opensource.org/licenses/EPL-2.0 Bundle-Version: 1.0.0.${tstamp} Export-Package: \ io.openems.backend.common.session,\ - io.openems.backend.common.component + io.openems.backend.common.component,\ + io.openems.backend.common.jsonrpc.request,\ + io.openems.backend.common.jsonrpc.response -includeresource: {readme.md} -buildpath: ${buildpath},\ io.openems.common;version=latest,\ + com.google.gson,\ + com.google.guava,\ slf4j.api -testpath: ${testpath} diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/GetEdgesChannelsValuesRequest.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesChannelsValuesRequest.java similarity index 97% rename from io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/GetEdgesChannelsValuesRequest.java rename to io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesChannelsValuesRequest.java index 35c1c36814d..a17820842c8 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/GetEdgesChannelsValuesRequest.java +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesChannelsValuesRequest.java @@ -1,4 +1,4 @@ -package io.openems.backend.b2bwebsocket.jsonrpc.request; +package io.openems.backend.common.jsonrpc.request; import java.util.TreeSet; import java.util.UUID; diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/GetEdgesStatusRequest.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesStatusRequest.java similarity index 93% rename from io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/GetEdgesStatusRequest.java rename to io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesStatusRequest.java index c74100e005c..3805c0eedbb 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/GetEdgesStatusRequest.java +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesStatusRequest.java @@ -1,4 +1,4 @@ -package io.openems.backend.b2bwebsocket.jsonrpc.request; +package io.openems.backend.common.jsonrpc.request; import java.util.UUID; diff --git a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/package-info.java new file mode 100644 index 00000000000..a84938b0a42 --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.common.jsonrpc.request; diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/response/GetEdgesChannelsValuesResponse.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/GetEdgesChannelsValuesResponse.java similarity index 91% rename from io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/response/GetEdgesChannelsValuesResponse.java rename to io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/GetEdgesChannelsValuesResponse.java index 55d86d6dbde..a1c42bb4d93 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/response/GetEdgesChannelsValuesResponse.java +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/GetEdgesChannelsValuesResponse.java @@ -1,4 +1,4 @@ -package io.openems.backend.b2bwebsocket.jsonrpc.response; +package io.openems.backend.common.jsonrpc.response; import java.util.Map; import java.util.Map.Entry; @@ -9,7 +9,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import io.openems.backend.b2bwebsocket.jsonrpc.request.GetEdgesChannelsValuesRequest; +import io.openems.backend.common.jsonrpc.request.GetEdgesChannelsValuesRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.types.ChannelAddress; diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/response/GetEdgesStatusResponse.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/GetEdgesStatusResponse.java similarity index 90% rename from io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/response/GetEdgesStatusResponse.java rename to io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/GetEdgesStatusResponse.java index 7bf1c59a423..3e63200a0cb 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/response/GetEdgesStatusResponse.java +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/GetEdgesStatusResponse.java @@ -1,4 +1,4 @@ -package io.openems.backend.b2bwebsocket.jsonrpc.response; +package io.openems.backend.common.jsonrpc.response; import java.util.Map; import java.util.Map.Entry; @@ -6,7 +6,7 @@ import com.google.gson.JsonObject; -import io.openems.backend.b2bwebsocket.jsonrpc.request.GetEdgesStatusRequest; +import io.openems.backend.common.jsonrpc.request.GetEdgesStatusRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.utils.JsonUtils; diff --git a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/package-info.java new file mode 100644 index 00000000000..b655641ef78 --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/response/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.common.jsonrpc.response; diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnNotification.java b/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnNotification.java index 374cfa7dcb5..e28b207e130 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnNotification.java +++ b/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnNotification.java @@ -1,5 +1,7 @@ package io.openems.backend.edgewebsocket.impl; +import java.util.HashMap; +import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -11,11 +13,17 @@ import com.google.gson.JsonObject; import io.openems.backend.metadata.api.Edge; +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.JsonrpcNotification; import io.openems.common.jsonrpc.notification.EdgeConfigNotification; +import io.openems.common.jsonrpc.notification.EdgeRpcNotification; import io.openems.common.jsonrpc.notification.SystemLogNotification; import io.openems.common.jsonrpc.notification.TimestampedDataNotification; +import io.openems.common.types.ChannelAddress; +import io.openems.common.types.EdgeConfig; +import io.openems.common.types.EdgeConfig.Component; +import io.openems.common.types.EdgeConfig.Component.Channel; import io.openems.common.types.SemanticVersion; import io.openems.common.utils.JsonUtils; @@ -72,8 +80,13 @@ public void run(WebSocket ws, JsonrpcNotification notification) throws OpenemsNa private void handleEdgeConfigNotification(EdgeConfigNotification message, WsData wsData) throws OpenemsNamedException { String edgeId = wsData.assertEdgeId(message); + + // save config in metadata Edge edge = this.parent.metadata.getEdgeOrError(edgeId); edge.setConfig(message.getConfig()); + + // forward + this.parent.uiWebsocket.send(edgeId, new EdgeRpcNotification(edgeId, message)); } /** @@ -119,6 +132,47 @@ private void handleTimestampedDataNotification(TimestampedDataNotification messa String version = JsonUtils.getAsPrimitive(data, "_meta/Version").getAsString(); edge.setVersion(SemanticVersion.fromString(version)); } + if (data.has("_sum/State")) { + // Read global State + Optional levelOpt = Level.fromJson(data, "_sum/State"); + Map activeStateChannels = new HashMap<>(); + if (levelOpt.isPresent() && levelOpt.get() != Level.OK) { + // Global State is not "OK" -> Some State-Channel has to be active: + for (Entry componentEntry : edge.getConfig().getComponents().entrySet()) { + String componentId = componentEntry.getKey(); + // Get State-Level of this Component + Optional componentStateOpt = this.parent.timedata.getChannelValue(edgeId, + new ChannelAddress(componentId, "State")); + if (!componentStateOpt.isPresent()) { + continue; + } + Optional componentLevelOpt = Level.fromJson(componentStateOpt.get()); + if (!componentLevelOpt.isPresent() || componentLevelOpt.get() == Level.OK) { + continue; + } + // This Components state is not OK -> search for active State-Channels + for (Entry channelEntry : componentEntry.getValue().getStateChannels() + .entrySet()) { + String channelId = channelEntry.getKey(); + Optional valueOptJ = this.parent.timedata.getChannelValue(edgeId, + new ChannelAddress(componentId, channelId)); + if (!valueOptJ.isPresent()) { + continue; + } + Optional valueOpt = JsonUtils.getAsOptionalInt(valueOptJ.get()); + if (!valueOpt.isPresent()) { + continue; + } + if (valueOpt.get() == 1 /* Booleans are transferred as '0' or '1' */) { + activeStateChannels.put(// + new ChannelAddress(componentId, channelId), // + channelEntry.getValue()); + } + } + } + } + edge.setSumState(levelOpt.orElse(null), activeStateChannels); + } } } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java index 6954567a4de..64ce15e8dd4 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java @@ -2,8 +2,12 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.BiConsumer; import java.util.function.Consumer; import org.slf4j.Logger; @@ -11,6 +15,8 @@ import com.google.gson.JsonObject; +import io.openems.common.channel.Level; +import io.openems.common.types.ChannelAddress; import io.openems.common.types.EdgeConfig; import io.openems.common.types.SemanticVersion; import io.openems.common.utils.JsonUtils; @@ -34,10 +40,11 @@ public enum State { private ZonedDateTime lastUpdate = null; private Integer soc = null; private String ipv4 = null; - private boolean isOnline; + private Level sumState = null; + private boolean isOnline = false; public Edge(String id, String apikey, String comment, State state, String version, String producttype, - EdgeConfig config, Integer soc, String ipv4) { + EdgeConfig config, Integer soc, String ipv4, Level sumState) { this.id = id; this.apikey = apikey; this.comment = comment; @@ -47,6 +54,7 @@ public Edge(String id, String apikey, String comment, State state, String versio this.config = config; this.soc = soc; this.ipv4 = ipv4; + this.sumState = sumState; } public String getApikey() { @@ -199,7 +207,8 @@ public void onSetVersion(Consumer listener) { public synchronized void setVersion(SemanticVersion version) { if (this.version == null || !version.equals(this.version)) { // on change - this.log.info("Edge [" + this.getId() + "]: Update version to [" + version + "]. It was [" + this.version + "]"); + this.log.info( + "Edge [" + this.getId() + "]: Update version to [" + version + "]. It was [" + this.version + "]"); this.onSetVersion.forEach(listener -> listener.accept(version)); this.version = version; } @@ -239,4 +248,25 @@ public synchronized void setIpv4(String ipv4) { } } + /* + * _sum/State + */ + private final List>> onSetSumState = new CopyOnWriteArrayList<>(); + + public void onSetSumState(BiConsumer> listener) { + this.onSetSumState.add(listener); + } + + private Set lastActiveStateChannelsKeys = new HashSet<>(); + + public synchronized void setSumState(Level sumState, + Map activeStateChannels) { + if (this.sumState == null || !this.sumState.equals(sumState) + || !this.lastActiveStateChannelsKeys.equals(activeStateChannels.keySet())) { // on change + this.lastActiveStateChannelsKeys = activeStateChannels.keySet(); + this.onSetSumState.forEach(listener -> listener.accept(sumState, activeStateChannels)); + this.sumState = sumState; + } + } + } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Metadata.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Metadata.java index 26962f9f594..e8d344ed573 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Metadata.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Metadata.java @@ -1,13 +1,25 @@ package io.openems.backend.metadata.api; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; +import java.util.stream.Collectors; import org.osgi.annotation.versioning.ProviderType; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import com.google.common.collect.HashMultimap; + +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; +import io.openems.common.types.EdgeConfig; +import io.openems.common.types.EdgeConfig.Component.Channel; +import io.openems.common.types.EdgeConfig.Component.Channel.ChannelDetail; +import io.openems.common.types.EdgeConfig.Component.Channel.ChannelDetailState; @ProviderType public interface Metadata { @@ -92,4 +104,60 @@ public default Edge getEdgeOrError(String edgeId) throws OpenemsException { * @return collection of Edges. */ public abstract Collection getAllEdges(); + + /** + * Helper method for creating a String of all active State-Channels by Level. + * + * @param activeStateChannels Map of ChannelAddress and + * EdgeConfig.Component.Channel; as returned by + * Edge.onSetSumState() + * @return a string + */ + public static String activeStateChannelsToString( + Map activeStateChannels) { + // Sort active State-Channels by Level and Component-ID + HashMap> states = new HashMap<>(); + for (Entry entry : activeStateChannels.entrySet()) { + ChannelDetail detail = entry.getValue().getDetail(); + if (detail instanceof ChannelDetailState) { + Level level = ((ChannelDetailState) detail).getLevel(); + HashMultimap channelsByComponent = states.get(level); + if (channelsByComponent == null) { + channelsByComponent = HashMultimap.create(); + states.put(level, channelsByComponent); + } + channelsByComponent.put(// + entry.getKey().getComponentId(), // + entry.getValue()); + } + } + StringBuilder result = new StringBuilder(); + for (Level level : Level.values()) { + HashMultimap channelsByComponent = states.get(level); + if (channelsByComponent != null) { + if (result.length() > 0) { + result.append("| "); + } + result.append(level.name() + ": "); + StringBuilder subResult = new StringBuilder(); + for (Entry> entry : channelsByComponent.asMap().entrySet()) { + if (subResult.length() > 0) { + subResult.append("; "); + } + subResult.append(entry.getKey() + ": "); + subResult.append(entry.getValue().stream() // + .map(channel -> { + if (!channel.getText().isEmpty()) { + return channel.getText(); + } else { + return channel.getId(); + } + }) // + .collect(Collectors.joining(", "))); + } + result.append(subResult); + } + } + return result.toString(); + } } diff --git a/io.openems.backend.metadata.dummy/bnd.bnd b/io.openems.backend.metadata.dummy/bnd.bnd index 8e2427b73e1..4d5c520c36c 100644 --- a/io.openems.backend.metadata.dummy/bnd.bnd +++ b/io.openems.backend.metadata.dummy/bnd.bnd @@ -7,13 +7,15 @@ Private-Package: io.openems.backend.metadata.dummy -includeresource: {readme.md} --buildpath: ${buildpath},\ +-buildpath: \ + ${buildpath},\ io.openems.backend.common;version=latest,\ io.openems.backend.edgewebsocket.api;version=latest,\ io.openems.backend.metadata.api;version=latest,\ io.openems.common;version=latest,\ com.google.gson,\ - slf4j.api + slf4j.api,\ + com.google.guava -testpath: ${testpath} diff --git a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/Dummy.java b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/Dummy.java index 80544765f87..98d38030c6b 100644 --- a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/Dummy.java +++ b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/Dummy.java @@ -99,7 +99,8 @@ public Optional getEdgeIdForApikey(String apikey) { id = this.nextEdgeId.incrementAndGet(); edgeId = "edge" + id; } - Edge edge = new Edge(edgeId, apikey, "OpenEMS Edge #" + id, State.ACTIVE, "", "", new EdgeConfig(), null, null); + Edge edge = new Edge(edgeId, apikey, "OpenEMS Edge #" + id, State.ACTIVE, "", "", new EdgeConfig(), null, null, + null); edge.onSetConfig(config -> { this.logInfo(this.log, "Edge [" + edgeId + "]. Update config: " + StringUtils.toShortString(EdgeConfigDiff.diff(config, edge.getConfig()).getAsHtml(), 100)); @@ -110,8 +111,20 @@ public Optional getEdgeIdForApikey(String apikey) { edge.onSetIpv4(ipv4 -> { this.logInfo(this.log, "Edge [" + edgeId + "]. Set IPv4: " + ipv4); }); + edge.onSetSumState((sumState, activeStateChannels) -> { + String sumStateString; + if (sumState != null) { + sumStateString = sumState.getName().toLowerCase(); + } else { + sumStateString = ""; + } + String states = Metadata.activeStateChannelsToString(activeStateChannels); + this.logInfo(this.log, + "Edge [" + edgeId + "]. Set State \"" + sumStateString + "\". Long-Text: " + states); + }); this.edges.put(edgeId, edge); return Optional.ofNullable(edgeId); + } @Override diff --git a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/File.java b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/File.java index dde026ba71d..0b9828e5d82 100644 --- a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/File.java +++ b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/File.java @@ -162,7 +162,8 @@ private synchronized void refreshData() { "", // Product-Type new EdgeConfig(), // Config null, // State of Charge - null // IPv4 + null, // IPv4 + null // _sum/State )); } } catch (OpenemsNamedException e) { diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Config.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Config.java index b0d35a111c8..c91c59842f1 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Config.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Config.java @@ -3,7 +3,7 @@ import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; -@ObjectClassDefinition( // +@ObjectClassDefinition(// name = "Metadata.Odoo", // description = "Configures the Odoo Metadata provider") @interface Config { diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java index 9974f1acbc8..c651284d99e 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java @@ -10,20 +10,20 @@ public class EdgeCache { /** - * Maps Edge-ID to Edge + * Maps Edge-ID to Edge. */ private ConcurrentHashMap edgeIdToEdge = new ConcurrentHashMap<>(); /** - * Maps Odoo-ID to Edge-ID + * Maps Odoo-ID to Edge-ID. */ private ConcurrentHashMap odooIdToEdgeId = new ConcurrentHashMap<>(); /** - * Maps API-Key to Edge-ID + * Maps API-Key to Edge-ID. */ private ConcurrentHashMap apikeyToEdgeId = new ConcurrentHashMap<>(); /** - * Adds an Edge to the Cache + * Adds an Edge to the Cache. * * @param edge the Edge */ diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java index 51b4b9d5094..4638c165cc4 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java @@ -16,7 +16,10 @@ public enum EdgeDevice implements Field { LAST_MESSAGE("lastmessage"), // LAST_UPDATE("lastupdate"), // SOC("soc"), // - IPV4("ipv4"); + IPV4("ipv4"), // + OPENEMS_SUM_STATE("openems_sum_state"), // + OPENEMS_SUM_STATE_TEXT("openem_sum_state_text"), // + OPENEMS_IS_CONNECTED("openems_is_connected"); private final String n; diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/FieldValue.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/FieldValue.java index de8bb000efc..e8dd4935f42 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/FieldValue.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/FieldValue.java @@ -1,10 +1,10 @@ package io.openems.backend.metadata.odoo; -public class FieldValue { +public class FieldValue { private final Field field; - private final String value; + private final T value; - public FieldValue(Field field, String value) { + public FieldValue(Field field, T value) { this.field = field; this.value = value; } @@ -13,7 +13,7 @@ public Field getField() { return field; } - public String getValue() { + public T getValue() { return value; } diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java index 8c6650f516c..0cef5561599 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java @@ -1,6 +1,7 @@ package io.openems.backend.metadata.odoo; import io.openems.backend.metadata.api.Edge; +import io.openems.common.channel.Level; import io.openems.common.types.EdgeConfig; public class MyEdge extends Edge { @@ -8,12 +9,13 @@ public class MyEdge extends Edge { private final int odooId; public MyEdge(int odooId, String edgeId, String apikey, String comment, State state, String version, - String producttype, EdgeConfig config, Integer soc, String ipv4) { - super(edgeId, apikey, comment, state, version, producttype, config, soc, ipv4); + String producttype, EdgeConfig config, Integer soc, String ipv4, Level sumState) { + super(edgeId, apikey, comment, state, version, producttype, config, soc, ipv4, sumState); this.odooId = odooId; } public int getOdooId() { - return odooId; + return this.odooId; } + } diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Odoo.java index fd7aedbae71..09bab8dd21a 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Odoo.java @@ -33,6 +33,7 @@ import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.types.EdgeConfig; import io.openems.common.types.EdgeConfigDiff; +import io.openems.common.types.EdgeConfig.Component.JsonFormat; import io.openems.common.utils.JsonUtils; import io.openems.common.utils.StringUtils; @@ -42,8 +43,8 @@ public class Odoo extends AbstractOpenemsBackendComponent implements Metadata { public final static String ODOO_MODEL = "edge.device"; - private final static int READ_BATCH_SIZE = 300; - private final static int MAX_TRIES = 10; + private static final int READ_BATCH_SIZE = 300; + private static final int MAX_TRIES = 10; private final Logger log = LoggerFactory.getLogger(Odoo.class); private final OdooWriteWorker writeWorker; @@ -51,11 +52,11 @@ public class Odoo extends AbstractOpenemsBackendComponent implements Metadata { private OdooCredentials odooCredentials; /** - * Maps User-ID to User + * Maps User-ID to User. */ private ConcurrentHashMap users = new ConcurrentHashMap<>(); /** - * Caches Edges + * Caches Edges. */ private EdgeCache edges = new EdgeCache(); @@ -122,7 +123,8 @@ void deactivate() { new Field[] { Field.EdgeDevice.ID, Field.EdgeDevice.APIKEY, Field.EdgeDevice.NAME, Field.EdgeDevice.COMMENT, Field.EdgeDevice.OPENEMS_VERSION, Field.EdgeDevice.PRODUCT_TYPE, Field.EdgeDevice.OPENEMS_CONFIG, - Field.EdgeDevice.SOC, Field.EdgeDevice.IPV4, Field.EdgeDevice.STATE }); + Field.EdgeDevice.SOC, Field.EdgeDevice.IPV4, Field.EdgeDevice.STATE, + Field.EdgeDevice.OPENEMS_SUM_STATE, Field.EdgeDevice.OPENEMS_IS_CONNECTED }); retry = false; } catch (OpenemsException e) { this.logError(this.log, "Unable to read Edges from Odoo: " + e.getMessage()); @@ -140,12 +142,6 @@ void deactivate() { // simple fields Integer odooId = OdooUtils.getAsInteger(edgeMap.get(Field.EdgeDevice.ID.n())); String edgeId = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.NAME.n())); - String apikey = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.APIKEY.n())); - String comment = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.COMMENT.n())); - String version = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.OPENEMS_VERSION.n())); - String productType = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.PRODUCT_TYPE.n())); - String initialIpv4 = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.IPV4.n())); - Integer initialSoc = OdooUtils.getAsInteger(edgeMap.get(Field.EdgeDevice.SOC.n())); // Config EdgeConfig config; @@ -174,10 +170,18 @@ void deactivate() { state = State.INACTIVE; // Default } + // more simple fields + String apikey = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.APIKEY.n())); + String comment = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.COMMENT.n())); + String version = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.OPENEMS_VERSION.n())); + String productType = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.PRODUCT_TYPE.n())); + String initialIpv4 = OdooUtils.getAsString(edgeMap.get(Field.EdgeDevice.IPV4.n())); + Integer initialSoc = OdooUtils.getAsInteger(edgeMap.get(Field.EdgeDevice.SOC.n())); + // Create instance of Edge and register listeners MyEdge edge = new MyEdge(// odooId, // - edgeId, apikey, comment, state, version, productType, config, initialSoc, initialIpv4); + edgeId, apikey, comment, state, version, productType, config, initialSoc, initialIpv4, null); this.addListeners(edge); // store in cache @@ -194,12 +198,21 @@ void deactivate() { */ private void addListeners(MyEdge edge) { edge.onSetOnline(isOnline -> { - if (isOnline && edge.getState().equals(State.INACTIVE)) { - // Update Edge state to active - this.logInfo(this.log, - "Mark Edge [" + edge.getId() + "] as ACTIVE. It was [" + edge.getState().name() + "]"); - edge.setState(State.ACTIVE); - this.write(edge, new FieldValue(Field.EdgeDevice.STATE, "active")); + if (isOnline) { + // Edge came Online + this.write(edge, new FieldValue(Field.EdgeDevice.OPENEMS_IS_CONNECTED, true)); + + if (edge.getState().equals(State.INACTIVE)) { + // Edge was Inactive -> Update state to active + this.logInfo(this.log, + "Mark Edge [" + edge.getId() + "] as ACTIVE. It was [" + edge.getState().name() + "]"); + edge.setState(State.ACTIVE); + this.write(edge, new FieldValue(Field.EdgeDevice.STATE, "active")); + } + + } else { + // Edge disconnected + this.write(edge, new FieldValue(Field.EdgeDevice.OPENEMS_IS_CONNECTED, false)); } }); edge.onSetConfig(config -> { @@ -212,10 +225,11 @@ private void addListeners(MyEdge edge) { this.logDebug(this.log, "Edge [" + edge.getId() + "]. Update config: " + StringUtils.toShortString(diff.toString(), 100)); String conf = new GsonBuilder().setPrettyPrinting().create().toJson(config.toJson()); - String components = new GsonBuilder().setPrettyPrinting().create().toJson(config.componentsToJson()); + String components = new GsonBuilder().setPrettyPrinting().create() + .toJson(config.componentsToJson(JsonFormat.WITHOUT_CHANNELS)); this.write(edge, // - new FieldValue(Field.EdgeDevice.OPENEMS_CONFIG, conf), - new FieldValue(Field.EdgeDevice.OPENEMS_CONFIG_COMPONENTS, components)); + new FieldValue(Field.EdgeDevice.OPENEMS_CONFIG, conf), + new FieldValue(Field.EdgeDevice.OPENEMS_CONFIG_COMPONENTS, components)); // write EdgeConfig-Diff to Odoo Chatter try { @@ -238,15 +252,29 @@ private void addListeners(MyEdge edge) { // Set Version in Odoo this.logInfo(this.log, "Edge [" + edge.getId() + "]: Update OpenEMS Edge version to [" + version + "]. It was [" + edge.getVersion() + "]"); - this.write(edge, new FieldValue(Field.EdgeDevice.OPENEMS_VERSION, version.toString())); + this.write(edge, new FieldValue(Field.EdgeDevice.OPENEMS_VERSION, version.toString())); }); edge.onSetSoc(soc -> { // Set SoC in Odoo - this.write(edge, new FieldValue(Field.EdgeDevice.SOC, String.valueOf(soc))); + this.write(edge, new FieldValue(Field.EdgeDevice.SOC, String.valueOf(soc))); }); edge.onSetIpv4(ipv4 -> { // Set IPv4 in Odoo - this.write(edge, new FieldValue(Field.EdgeDevice.IPV4, String.valueOf(ipv4))); + this.write(edge, new FieldValue(Field.EdgeDevice.IPV4, String.valueOf(ipv4))); + }); + edge.onSetSumState((sumState, activeStateChannels) -> { + // Set "_sum/State" in Odoo + String sumStateString; + if (sumState != null) { + sumStateString = sumState.getName().toLowerCase(); + } else { + sumStateString = ""; + } + String states = Metadata.activeStateChannelsToString(activeStateChannels); + this.write(edge, // + new FieldValue(Field.EdgeDevice.OPENEMS_SUM_STATE, sumStateString), // + new FieldValue(Field.EdgeDevice.OPENEMS_SUM_STATE_TEXT, states)); + }); } @@ -264,9 +292,9 @@ public BackendUser authenticate(String username, String password) throws Openems /** * Tries to authenticate at the Odoo server using a sessionId from a cookie. * - * @param sessionId - * @return - * @throws OpenemsException + * @param sessionId the Session-ID + * @return the BackendUser + * @throws OpenemsException on error */ @Override public BackendUser authenticate(String sessionId) throws OpenemsNamedException { @@ -291,10 +319,10 @@ public BackendUser authenticate(String sessionId) throws OpenemsNamedException { /** * Writes one field to Odoo. * - * @param edge the Edge - * @param fieldValue the FieldValue + * @param edge the Edge + * @param fieldValues the FieldValues */ - private void write(MyEdge edge, FieldValue... fieldValues) { + private void write(MyEdge edge, FieldValue... fieldValues) { try { OdooUtils.write(this.odooCredentials, ODOO_MODEL, new Integer[] { edge.getOdooId() }, fieldValues); } catch (OpenemsException e) { diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooUtils.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooUtils.java index 2e3f7d2f3ba..450c532646e 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooUtils.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooUtils.java @@ -327,7 +327,7 @@ protected static Map[] searchRead(OdooCredentials credentials, S * @param credentials the Odoo credentials * @param model Odoo model (e.g. 'res.partner') * @param id id of model - * @param fields fields that should be read + * @param message the message * @throws OpenemsException on error */ protected static void addChatterMessage(OdooCredentials credentials, String model, int id, String message) @@ -355,7 +355,7 @@ protected static void addChatterMessage(OdooCredentials credentials, String mode * @param fieldValues fields and values that should be written * @throws OpenemsException on error */ - protected static void write(OdooCredentials credentials, String model, Integer[] ids, FieldValue... fieldValues) + protected static void write(OdooCredentials credentials, String model, Integer[] ids, FieldValue... fieldValues) throws OpenemsException { // // for debugging: // StringBuilder b = new StringBuilder("Odoo Write: " + model + "; "); @@ -372,7 +372,7 @@ protected static void write(OdooCredentials credentials, String model, Integer[] String action = "write"; // Add fieldValues Map paramsFieldValues = new HashMap<>(); - for (FieldValue fieldValue : fieldValues) { + for (FieldValue fieldValue : fieldValues) { paramsFieldValues.put(fieldValue.getField().n(), fieldValue.getValue()); } // Create request params diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooWriteWorker.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooWriteWorker.java index 29518ce1278..55489a15056 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooWriteWorker.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooWriteWorker.java @@ -24,18 +24,18 @@ */ public class OdooWriteWorker { - private final static int UPDATE_INTERVAL_IN_SECONDS = 60; + private static final int UPDATE_INTERVAL_IN_SECONDS = 60; private final Logger log = LoggerFactory.getLogger(OdooWriteWorker.class); private final Odoo parent; /** - * Holds the scheduled task + * Holds the scheduled task. */ private ScheduledFuture future = null; /** - * Executor for subscriptions task + * Executor for subscriptions task. */ private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); @@ -84,7 +84,7 @@ public synchronized void stop() { if (ids.length > 0) { try { OdooUtils.write(odooCredentials, Odoo.ODOO_MODEL, ids, - new FieldValue(Field.EdgeDevice.LAST_MESSAGE, time)); + new FieldValue(Field.EdgeDevice.LAST_MESSAGE, time)); } catch (OpenemsException e) { log.error("Unable to write lastMessage: " + e.getMessage()); } @@ -99,7 +99,7 @@ public synchronized void stop() { if (ids.length > 0) { try { OdooUtils.write(odooCredentials, Odoo.ODOO_MODEL, ids, - new FieldValue(Field.EdgeDevice.LAST_UPDATE, time)); + new FieldValue(Field.EdgeDevice.LAST_UPDATE, time)); } catch (OpenemsException e) { log.error("Unable to write lastUpdate: " + e.getMessage()); } diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/AuthenticateWithSessionIdResponse.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/AuthenticateWithSessionIdResponse.java index 83527403e19..c11f78d7e18 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/AuthenticateWithSessionIdResponse.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/AuthenticateWithSessionIdResponse.java @@ -42,7 +42,7 @@ */ public class AuthenticateWithSessionIdResponse extends JsonrpcResponseSuccess { - private final static Logger log = LoggerFactory.getLogger(AuthenticateWithSessionIdResponse.class); + private static final Logger log = LoggerFactory.getLogger(AuthenticateWithSessionIdResponse.class); public static AuthenticateWithSessionIdResponse from(JsonrpcResponseSuccess response, String sessionId, EdgeCache edges) throws OpenemsNamedException { diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/OdooCallRequest.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/OdooCallRequest.java index 942f6c58177..b1a65683675 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/OdooCallRequest.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/jsonrpc/OdooCallRequest.java @@ -18,7 +18,7 @@ */ public abstract class OdooCallRequest extends JsonrpcRequest { - public final static String METHOD = "call"; + public static final String METHOD = "call"; public OdooCallRequest(UUID id) { super(id, METHOD); diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Config.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Config.java index 33c07d4495f..965f197a553 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Config.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Config.java @@ -25,6 +25,9 @@ @AttributeDefinition(name = "Measurement", description = "The InfluxDB measurement") String measurement() default "data"; + + @AttributeDefinition(name = "Retention-Policy", description = "The InfluxDB retention policy") + String retentionPolicy() default "autogen"; @AttributeDefinition(name = "Read-Only mode", description = "Activates the read-only mode. Then no data is written to InfluxDB.") boolean isReadOnly() default false; diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java index 0d7e95f112b..7780e9b1bd0 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java @@ -10,8 +10,9 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; -import org.influxdb.dto.BatchPoints; import org.influxdb.dto.Point; import org.influxdb.dto.Point.Builder; import org.osgi.service.component.annotations.Activate; @@ -38,6 +39,7 @@ import io.openems.common.types.ChannelAddress; import io.openems.common.types.EdgeConfig; import io.openems.common.types.SemanticVersion; +import io.openems.common.utils.StringUtils; import io.openems.shared.influxdb.InfluxConnector; import io.openems.shared.influxdb.InfluxConstants; @@ -65,6 +67,7 @@ void activate(Config config) throws OpenemsException { "url=" + config.url() + // ";port=" + config.port() + // ";database=" + config.database() + // + ";retentionPolicy=" + config.retentionPolicy() + // ";username=" + config.username() + // ";password=" + (config.password() != null ? "ok" : "NOT_SET") + // ";measurement=" + config.measurement() + // @@ -72,7 +75,13 @@ void activate(Config config) throws OpenemsException { "]"); this.influxConnector = new InfluxConnector(config.url(), config.port(), config.username(), config.password(), - config.database(), config.isReadOnly()); + config.database(), config.retentionPolicy(), config.isReadOnly(), // + (failedPoints, throwable) -> { + String pointsString = StreamSupport.stream(failedPoints.spliterator(), false) + .map(Point::lineProtocol).collect(Collectors.joining(",")); + this.logError(this.log, "Unable to write to InfluxDB: " + throwable.getMessage() + " for " + + StringUtils.toShortString(pointsString, 100)); + }); } @Deactivate @@ -159,10 +168,6 @@ private void writeData(int influxEdgeId, TreeBasedTable> dataEntry : dataEntries) { Set> channelEntries = dataEntry.getValue().entrySet(); if (channelEntries.isEmpty()) { @@ -172,22 +177,17 @@ private void writeData(int influxEdgeId, TreeBasedTable channelEntry : channelEntries) { Influx.addValue(builder, channelEntry.getKey().toString(), channelEntry.getValue()); } if (builder.hasFields()) { - batchPoints.point(builder.build()); + this.influxConnector.write(builder.build()); } } - - if (batchPoints.getPoints().isEmpty()) { - // no points added - return; - } - - // write to DB - this.influxConnector.write(batchPoints); } public static Integer parseNumberFromName(String name) throws OpenemsException { diff --git a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocket.java b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocket.java index 570811949f0..1dcd9e8472c 100644 --- a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocket.java @@ -34,4 +34,14 @@ public CompletableFuture send(UUID token, JsonrpcRequest */ public void send(UUID token, JsonrpcNotification notification) throws OpenemsNamedException; + /** + * Send a JSON-RPC Notification broadcast to all UI sessions with a given + * Edge-ID. + * + * @param edgeId the Edge-ID + * @param notification the JsonrpcNotification + * @throws OpenemsNamedException on error + */ + public void send(String edgeId, JsonrpcNotification notification) throws OpenemsNamedException; + } diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnRequest.java b/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnRequest.java index c203e3d4474..676eb531098 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnRequest.java +++ b/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnRequest.java @@ -14,17 +14,19 @@ import com.google.gson.JsonElement; import io.openems.backend.metadata.api.BackendUser; -import io.openems.common.OpenemsConstants; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; +import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; +import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; import io.openems.common.jsonrpc.request.EdgeRpcRequest; import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesDataRequest; import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesEnergyRequest; +import io.openems.common.jsonrpc.request.SetChannelValueRequest; import io.openems.common.jsonrpc.request.SubscribeChannelsRequest; import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; @@ -128,11 +130,25 @@ private CompletableFuture handleEdgeRpcRequest(WsData wsData, B resultFuture = this.handleGetEdgeConfigRequest(edgeId, user, GetEdgeConfigRequest.from(request)); break; + case CreateComponentConfigRequest.METHOD: + resultFuture = this.handleCreateComponentConfigRequest(edgeId, user, + CreateComponentConfigRequest.from(request)); + break; + case UpdateComponentConfigRequest.METHOD: resultFuture = this.handleUpdateComponentConfigRequest(edgeId, user, UpdateComponentConfigRequest.from(request)); break; + case DeleteComponentConfigRequest.METHOD: + resultFuture = this.handleDeleteComponentConfigRequest(edgeId, user, + DeleteComponentConfigRequest.from(request)); + break; + + case SetChannelValueRequest.METHOD: + resultFuture = this.handleSetChannelValueRequest(edgeId, user, SetChannelValueRequest.from(request)); + break; + case ComponentJsonApiRequest.METHOD: resultFuture = this.handleComponentJsonApiRequest(edgeId, user, ComponentJsonApiRequest.from(request)); break; @@ -249,6 +265,22 @@ private CompletableFuture handleGetEdgeConfigRequest(Str return CompletableFuture.completedFuture(new GetEdgeConfigResponse(request.getId(), config)); } + /** + * Handles a CreateComponentConfigRequest. + * + * @param edgeId the Edge-ID + * @param user the User - Installer-level required + * @param createComponentConfigRequest the CreateComponentConfigRequest + * @return the Future JSON-RPC Response + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleCreateComponentConfigRequest(String edgeId, User user, + CreateComponentConfigRequest request) throws OpenemsNamedException { + user.assertRoleIsAtLeast(CreateComponentConfigRequest.METHOD, Role.INSTALLER); + + return this.parent.edgeWebsocket.send(edgeId, user, request); + } + /** * Handles a UpdateComponentConfigRequest. * @@ -259,12 +291,39 @@ private CompletableFuture handleGetEdgeConfigRequest(Str * @throws OpenemsNamedException on error */ private CompletableFuture handleUpdateComponentConfigRequest(String edgeId, User user, - UpdateComponentConfigRequest updateComponentConfigRequest) throws OpenemsNamedException { - user.assertRoleIsAtLeast(UpdateComponentConfigRequest.METHOD, Role.INSTALLER); + UpdateComponentConfigRequest request) throws OpenemsNamedException { + user.assertRoleIsAtLeast(UpdateComponentConfigRequest.METHOD, Role.OWNER); - // wrap original request inside ComponentJsonApiRequest - String componentId = OpenemsConstants.COMPONENT_MANAGER_ID; - ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, updateComponentConfigRequest); + return this.parent.edgeWebsocket.send(edgeId, user, request); + } + + /** + * Handles a DeleteComponentConfigRequest. + * + * @param edgeId the Edge-ID + * @param user the User - Installer-level required + * @param updateComponentConfigRequest the DeleteComponentConfigRequest + * @return the Future JSON-RPC Response + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleDeleteComponentConfigRequest(String edgeId, User user, + DeleteComponentConfigRequest request) throws OpenemsNamedException { + user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.INSTALLER); + + return this.parent.edgeWebsocket.send(edgeId, user, request); + } + + /** + * Handles a SetChannelValueRequest. + * + * @param user the User + * @param request the SetChannelValueRequest + * @return the Future JSON-RPC Response + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleSetChannelValueRequest(String edgeId, User user, + SetChannelValueRequest request) throws OpenemsNamedException { + user.assertRoleIsAtLeast(SetChannelValueRequest.METHOD, Role.ADMIN); return this.parent.edgeWebsocket.send(edgeId, user, request); } diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java b/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java index abf28591876..cd83dba853e 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java +++ b/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java @@ -1,7 +1,9 @@ package io.openems.backend.uiwebsocket.impl; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -17,6 +19,7 @@ import io.openems.backend.common.component.AbstractOpenemsBackendComponent; import io.openems.backend.edgewebsocket.api.EdgeWebsocket; +import io.openems.backend.metadata.api.BackendUser; import io.openems.backend.metadata.api.Metadata; import io.openems.backend.timedata.api.Timedata; import io.openems.backend.uiwebsocket.api.UiWebsocket; @@ -25,6 +28,7 @@ import io.openems.common.jsonrpc.base.JsonrpcNotification; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.session.Role; @Designate(ocd = Config.class, factory = false) @Component(name = "Ui.Websocket", configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true) @@ -99,6 +103,14 @@ public CompletableFuture send(UUID token, JsonrpcRequest return wsData.send(request); } + @Override + public void send(String edgeId, JsonrpcNotification notification) throws OpenemsNamedException { + List wsDatas = this.getWsDatasForEdgeId(edgeId); + for (WsData wsData : wsDatas) { + wsData.send(notification); + } + } + /** * Gets the WebSocket connection attachment for a UI token. * @@ -118,4 +130,37 @@ private WsData getWsDataForTokenOrError(UUID token) throws OpenemsNamedException } throw OpenemsError.BACKEND_NO_UI_WITH_TOKEN.exception(token); } + + /** + * Gets the WebSocket connection attachments of all connections accessing an + * Edge-ID. + * + * @param edgeId the Edge-ID + * @return the WsDatas; empty list if there are none + */ + private List getWsDatasForEdgeId(String edgeId) { + List result = new ArrayList<>(); + Collection connections = this.server.getConnections(); + for (Iterator iter = connections.iterator(); iter.hasNext();) { + WebSocket websocket = iter.next(); + WsData wsData = websocket.getAttachment(); + // get attachment User-ID + Optional userIdOpt = wsData.getUserId(); + if (userIdOpt.isPresent()) { + String userId = userIdOpt.get(); + // get BackendUser for User-ID + Optional userOpt = this.metadata.getUser(userId); + if (userOpt.isPresent()) { + BackendUser user = userOpt.get(); + Optional edgeRoleOpt = user.getEdgeRole(edgeId); + if (edgeRoleOpt.isPresent()) { + // User has access to this Edge-ID + result.add(wsData); + } + } + } + } + return result; + } + } diff --git a/io.openems.common/.settings/org.eclipse.core.resources.prefs b/io.openems.common/.settings/org.eclipse.core.resources.prefs index b7701f5d0cb..86d092175a6 100644 --- a/io.openems.common/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.common/.settings/org.eclipse.core.resources.prefs @@ -1,4 +1,5 @@ eclipse.preferences.version=1 +encoding//src/io/openems/common/channel/Unit.java=UTF-8 encoding//test/.gitignore=UTF-8 encoding/bnd.bnd=UTF-8 encoding/readme.md=UTF-8 diff --git a/io.openems.common/bnd.bnd b/io.openems.common/bnd.bnd index 2dff6572739..a53ba06d748 100644 --- a/io.openems.common/bnd.bnd +++ b/io.openems.common/bnd.bnd @@ -3,21 +3,22 @@ Bundle-Vendor: FENECON GmbH Bundle-License: https://opensource.org/licenses/EPL-2.0 Bundle-Version: 1.0.0.${tstamp} Export-Package: \ - io.openems.common.session,\ - io.openems.common.utils,\ - io.openems.common.exceptions,\ - io.openems.common.types,\ + io.openems.common,\ io.openems.common.api,\ - io.openems.common.websocket,\ + io.openems.common.channel,\ io.openems.common.config,\ - io.openems.common,\ - io.openems.common.timedata,\ + io.openems.common.exceptions,\ io.openems.common.jsonrpc,\ io.openems.common.jsonrpc.base,\ io.openems.common.jsonrpc.notification,\ io.openems.common.jsonrpc.request,\ io.openems.common.jsonrpc.response,\ io.openems.common.jsonrpc.shared,\ + io.openems.common.session,\ + io.openems.common.timedata,\ + io.openems.common.types,\ + io.openems.common.utils,\ + io.openems.common.websocket,\ io.openems.common.worker -includeresource: {readme.md} diff --git a/io.openems.common/src/io/openems/common/OpenemsConstants.java b/io.openems.common/src/io/openems/common/OpenemsConstants.java index 5337e7dfe63..84ce75f8626 100644 --- a/io.openems.common/src/io/openems/common/OpenemsConstants.java +++ b/io.openems.common/src/io/openems/common/OpenemsConstants.java @@ -18,7 +18,7 @@ public class OpenemsConstants { * * This is usually the number of the sprint within the year */ - public final static short VERSION_MINOR = 2; + public final static short VERSION_MINOR = 3; /** * The patch version of OpenEMS. diff --git a/io.openems.common/src/io/openems/common/channel/AccessMode.java b/io.openems.common/src/io/openems/common/channel/AccessMode.java new file mode 100644 index 00000000000..f42a067beb8 --- /dev/null +++ b/io.openems.common/src/io/openems/common/channel/AccessMode.java @@ -0,0 +1,27 @@ +package io.openems.common.channel; + +public enum AccessMode { + + /** + * Read-Only + */ + READ_ONLY("RO"), + /** + * Read-Write + */ + READ_WRITE("RW"), + /** + * Write-Only + */ + WRITE_ONLY("WO"); + + private final String abbreviation; + + private AccessMode(String abbreviation) { + this.abbreviation = abbreviation; + } + + public String getAbbreviation() { + return abbreviation; + } +} diff --git a/io.openems.common/src/io/openems/common/channel/ChannelCategory.java b/io.openems.common/src/io/openems/common/channel/ChannelCategory.java new file mode 100644 index 00000000000..9dd0a3db82d --- /dev/null +++ b/io.openems.common/src/io/openems/common/channel/ChannelCategory.java @@ -0,0 +1,7 @@ +package io.openems.common.channel; + +public enum ChannelCategory { + OPENEMS_TYPE, // + ENUM, // + STATE; +} \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/channel/Debounce.java b/io.openems.common/src/io/openems/common/channel/Debounce.java new file mode 100644 index 00000000000..0a7e0f0e3fd --- /dev/null +++ b/io.openems.common/src/io/openems/common/channel/Debounce.java @@ -0,0 +1,22 @@ +package io.openems.common.channel; + +public enum Debounce { + /** + * Debounce-Setting: If the StateChannel value is continuously set to 'true' for + * configured times in a row, the value of the StateChannel is set to true; + * otherwise false. + */ + TRUE_VALUES_IN_A_ROW_TO_SET_TRUE, + /** + * Debounce-Setting: If the StateChannel value is continuously set to 'false' + * for configured times in a row, the value of the StateChannel is set to false; + * otherwise true. + */ + FALSE_VALUES_IN_A_ROW_TO_SET_FALSE, + /** + * Debounce-Setting: If the StateChannel value is continuously set to the same + * value for configured times in a row, the value of the StateChannel is set to + * that value; otherwise stays at the old value. + */ + SAME_VALUES_IN_A_ROW_TO_CHANGE +} diff --git a/io.openems.common/src/io/openems/common/channel/Level.java b/io.openems.common/src/io/openems/common/channel/Level.java new file mode 100644 index 00000000000..e3923e395f6 --- /dev/null +++ b/io.openems.common/src/io/openems/common/channel/Level.java @@ -0,0 +1,98 @@ +package io.openems.common.channel; + +import java.util.Optional; + +import com.google.gson.JsonElement; + +import io.openems.common.types.OptionsEnum; +import io.openems.common.utils.JsonUtils; + +/** + * Severity/visibility Level + */ +public enum Level implements OptionsEnum { + /** + * "OK" indicates, that everything is OK and there are no messages. + */ + OK(0, "Ok"), // + /** + * "Info" indicates, that everything is OK, but there is at least one + * informative messages available. + */ + INFO(1, "Info"), // + /** + * "Warning" indicates, that there is at least one warning message available. + */ + WARNING(2, "Warning"), // + /** + * "Fault" indicates, that there is at least one fault message available. + */ + FAULT(3, "Fault"); + + private final int value; + private final String name; + + private Level(int value, String name) { + this.value = value; + this.name = name; + } + + @Override + public int getValue() { + return value; + } + + @Override + public String getName() { + return name; + } + + @Override + public OptionsEnum getUndefined() { + return OK; + } + + /** + * Gets the Level from an integer value. + * + * @param value the integer value + * @return the Level + */ + public static Optional fromValue(int value) { + for (Level level : Level.values()) { + if (value == level.getValue()) { + return Optional.of(level); + } + } + return Optional.empty(); + } + + /** + * Gets the Level from a JsonObject. + * + * @param element the JsonObject + * @param memberName the name of the member of the JsonObject + * @return the Level + */ + public static Optional fromJson(JsonElement element, String memberName) { + Optional valueOpt = JsonUtils.getAsOptionalInt(element, memberName); + if (!valueOpt.isPresent()) { + return Optional.empty(); + } + return Level.fromValue(valueOpt.get()); + } + + /** + * Gets the Level from a JsonElement. + * + * @param element the JsonElement + * @return the Level + */ + public static Optional fromJson(JsonElement element) { + Optional valueOpt = JsonUtils.getAsOptionalInt(element); + if (!valueOpt.isPresent()) { + return Optional.empty(); + } + return Level.fromValue(valueOpt.get()); + } +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/Unit.java b/io.openems.common/src/io/openems/common/channel/Unit.java similarity index 99% rename from io.openems.edge.common/src/io/openems/edge/common/channel/Unit.java rename to io.openems.common/src/io/openems/common/channel/Unit.java index c7b51055cfd..993c74ff337 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/Unit.java +++ b/io.openems.common/src/io/openems/common/channel/Unit.java @@ -1,4 +1,4 @@ -package io.openems.edge.common.channel; +package io.openems.common.channel; import com.google.common.base.CaseFormat; diff --git a/io.openems.common/src/io/openems/common/exceptions/CheckedConsumer.java b/io.openems.common/src/io/openems/common/exceptions/CheckedConsumer.java new file mode 100644 index 00000000000..d06120b27cc --- /dev/null +++ b/io.openems.common/src/io/openems/common/exceptions/CheckedConsumer.java @@ -0,0 +1,19 @@ +package io.openems.common.exceptions; + +import java.util.function.Consumer; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; + +/** + * This interface is similar to the java.util interface {@link Consumer}. + * Difference is, that it allows the accept() method to throw an + * {@link OpenemsNamedException}. + * + * @param the accept methods argument type + */ +@FunctionalInterface +public interface CheckedConsumer { + + public void accept(T t) throws OpenemsNamedException; + +} diff --git a/io.openems.common/src/io/openems/common/exceptions/OpenemsError.java b/io.openems.common/src/io/openems/common/exceptions/OpenemsError.java index 80569bb8218..e19eab4c51d 100644 --- a/io.openems.common/src/io/openems/common/exceptions/OpenemsError.java +++ b/io.openems.common/src/io/openems/common/exceptions/OpenemsError.java @@ -51,6 +51,7 @@ public enum OpenemsError { * JSON Errors. 5000-5999 */ JSON_HAS_NO_MEMBER(5000, "JSON [%s] has no member [%s]"), // + JSON_NO_INTEGER(5019, "JSON [%s] is not an Integer"), // JSON_NO_INTEGER_MEMBER(5001, "JSON [%s:%s] is not an Integer"), // JSON_NO_OBJECT(5002, "JSON [%s] is not a JSON-Object"), // JSON_NO_OBJECT_MEMBER(5003, "JSON [%s] is not a JSON-Object"), // @@ -68,6 +69,7 @@ public enum OpenemsError { JSON_PARSE_ELEMENT_FAILED(5015, "JSON failed to parse [%s]. %s: %s"), // JSON_PARSE_FAILED(5016, "JSON failed to parse [%s]: %s"), // JSON_NO_FLOAT_MEMBER(5017, "JSON [%s:%s] is not a Float"), // + JSON_NO_ENUM_MEMBER(5018, "JSON [%s:%s] is not an Enum"), // ; /** diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/SetChannelValueRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/SetChannelValueRequest.java new file mode 100644 index 00000000000..a5a22764dd9 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/request/SetChannelValueRequest.java @@ -0,0 +1,80 @@ +package io.openems.common.jsonrpc.request; + +import java.util.UUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; + +/** + * Sets the write value of a Channel. + * + *
+ * {
+ *   "jsonrpc": "2.0",
+ *   "id": "UUID",
+ *   "method": "setChannelValue",
+ *   "params": {
+ *     "componentId": string,
+ *     "channelId": string,
+ *     "value": any
+ *   }
+ * }
+ * 
+ */ +public class SetChannelValueRequest extends JsonrpcRequest { + + public static SetChannelValueRequest from(JsonrpcRequest r) throws OpenemsNamedException { + JsonObject p = r.getParams(); + String componentId = JsonUtils.getAsString(p, "componentId"); + String channelId = JsonUtils.getAsString(p, "channelId"); + JsonElement value = JsonUtils.getSubElement(p, "value"); + return new SetChannelValueRequest(r.getId(), componentId, channelId, value); + } + + public final static String METHOD = "setChannelValue"; + + private final String componentId; + private final String channelId; + private final JsonElement value; + + public SetChannelValueRequest(String componentId, String channelId, JsonElement value) { + this(UUID.randomUUID(), componentId, channelId, value); + } + + public SetChannelValueRequest(UUID id, String componentId, String channelId, JsonElement value) { + super(id, METHOD); + this.componentId = componentId; + this.channelId = channelId; + this.value = value; + } + + @Override + public JsonObject getParams() { + return JsonUtils.buildJsonObject() // + .addProperty("componentId", this.componentId) // + .addProperty("channelId", this.channelId) // + .add("value", this.value) // + .build(); + } + + public String getComponentId() { + return componentId; + } + + public String getChannelId() { + return channelId; + } + + public ChannelAddress getChannelAddress() { + return new ChannelAddress(this.componentId, this.channelId); + } + + public JsonElement getValue() { + return value; + } +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/response/Base64PayloadResponse.java b/io.openems.common/src/io/openems/common/jsonrpc/response/Base64PayloadResponse.java new file mode 100644 index 00000000000..26ebeb4f46a --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/response/Base64PayloadResponse.java @@ -0,0 +1,40 @@ +package io.openems.common.jsonrpc.response; + +import java.util.Base64; +import java.util.UUID; + +import com.google.gson.JsonObject; + +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.utils.JsonUtils; + +/** + * Represents a JSON-RPC Response for a Base64-encoded payload. + * + *
+ * {
+ *   "jsonrpc": "2.0",
+ *   "id": "UUID",
+ *   "result": {
+ *     "payload": Base64-String
+ *   }
+ * }
+ * 
+ */ +public class Base64PayloadResponse extends JsonrpcResponseSuccess { + + private final String payload; + + public Base64PayloadResponse(UUID id, byte[] payload) { + super(id); + this.payload = Base64.getEncoder().encodeToString(payload); + } + + @Override + public JsonObject getResult() { + return JsonUtils.buildJsonObject() // + .addProperty("payload", this.payload) // + .build(); + } + +} diff --git a/io.openems.common/src/io/openems/common/types/EdgeConfig.java b/io.openems.common/src/io/openems/common/types/EdgeConfig.java index 0d6130527eb..2d194f8534e 100644 --- a/io.openems.common/src/io/openems/common/types/EdgeConfig.java +++ b/io.openems.common/src/io/openems/common/types/EdgeConfig.java @@ -1,36 +1,268 @@ package io.openems.common.types; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.TreeMap; +import java.util.stream.Collectors; import org.osgi.service.metatype.AttributeDefinition; import org.osgi.service.metatype.ObjectClassDefinition; +import com.google.common.base.CaseFormat; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.ChannelCategory; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.types.EdgeConfig.Factory.Property; +import io.openems.common.types.EdgeConfig.Component.JsonFormat; +import io.openems.common.exceptions.OpenemsException; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.JsonUtils.JsonObjectBuilder; /** * Holds the configuration of an Edge. */ public class EdgeConfig { + /** + * Represents an instance of an OpenEMS Component. + */ public static class Component { + /** + * Represents a Channel of an OpenEMS Component. + */ + public static class Channel { + + public static interface ChannelDetail { + ChannelCategory getCategory(); + + JsonObject toJson(); + } + + /** + * Channel-Details for OpenemsType-Channel. + */ + public static class ChannelDetailOpenemsType implements ChannelDetail { + + public ChannelDetailOpenemsType() { + } + + @Override + public ChannelCategory getCategory() { + return ChannelCategory.OPENEMS_TYPE; + } + + @Override + public JsonObject toJson() { + return new JsonObject(); + } + } + + /** + * Channel-Details for EnumChannel. + */ + public static class ChannelDetailEnum implements ChannelDetail { + + private final Map options; + + public ChannelDetailEnum(Map options) { + this.options = options; + } + + @Override + public ChannelCategory getCategory() { + return ChannelCategory.ENUM; + } + + public Map getOptions() { + return options; + } + + @Override + public JsonObject toJson() { + JsonObject options = new JsonObject(); + for (Entry entry : this.options.entrySet()) { + options.add(entry.getKey(), entry.getValue()); + } + return JsonUtils.buildJsonObject() // + .add("options", options) // + .build(); + } + } + + /** + * Channel-Details for StateChannel. + */ + public static class ChannelDetailState implements ChannelDetail { + + private final Level level; + + public ChannelDetailState(Level level) { + this.level = level; + } + + public Level getLevel() { + return level; + } + + @Override + public ChannelCategory getCategory() { + return ChannelCategory.STATE; + } + + @Override + public JsonObject toJson() { + return JsonUtils.buildJsonObject() // + .addProperty("level", this.level.name()) // + .build(); + } + } + + /** + * Creates a Channel from JSON. + * + * @param channelId the Channel-ID + * @param json the JSON + * @return the Channel + * @throws OpenemsNamedException on error + */ + public static Channel fromJson(String channelId, JsonElement json) throws OpenemsNamedException { + OpenemsType type = JsonUtils.getAsEnum(OpenemsType.class, json, "type"); + Optional accessModeAbbrOpt = JsonUtils.getAsOptionalString(json, "accessMode"); + AccessMode accessMode = AccessMode.READ_ONLY; + if (accessModeAbbrOpt.isPresent()) { + String accessModeAbbr = accessModeAbbrOpt.get(); + for (AccessMode thisAccessMode : AccessMode.values()) { + if (accessModeAbbr.equals(thisAccessMode.getAbbreviation())) { + accessMode = thisAccessMode; + break; + } + } + } + String text = JsonUtils.getAsOptionalString(json, "text").orElse(""); + Unit unit = JsonUtils.getAsOptionalEnum(Unit.class, json, "unit").orElse(Unit.NONE); + ChannelCategory category = JsonUtils.getAsOptionalEnum(ChannelCategory.class, json, "category") + .orElse(ChannelCategory.OPENEMS_TYPE); + ChannelDetail detail = null; + switch (category) { + case OPENEMS_TYPE: { + detail = new ChannelDetailOpenemsType(); + break; + } + + case ENUM: { + Map values = new HashMap<>(); + Optional optionsOpt = JsonUtils.getAsOptionalJsonObject(json, "options"); + if (optionsOpt.isPresent()) { + for (Entry entry : optionsOpt.get().entrySet()) { + values.put(entry.getKey(), entry.getValue()); + } + } + detail = new ChannelDetailEnum(values); + break; + } + + case STATE: { + Level level = JsonUtils.getAsEnum(Level.class, json, "level"); + detail = new ChannelDetailState(level); + break; + } + + default: + throw new OpenemsException("Unknown Category-Key [" + category + "]"); + } + return new Channel(channelId, type, accessMode, text, unit, detail); + } + + private final String id; + private final OpenemsType type; + private final AccessMode accessMode; + private final String text; + private final Unit unit; + private final ChannelDetail detail; + + public Channel(String id, OpenemsType type, AccessMode accessMode, String text, Unit unit, + ChannelDetail detail) { + this.id = id; + this.type = type; + this.accessMode = accessMode; + this.text = text; + this.unit = unit; + this.detail = detail; + } + + public String getId() { + return id; + } + + public OpenemsType getType() { + return type; + } + + public AccessMode getAccessMode() { + return accessMode; + } + + public String getText() { + return text; + } + + public Unit getUnit() { + return unit; + } + + public ChannelDetail getDetail() { + return detail; + } + + /** + * Gets the JSON representation of this Channel. + * + * @return a JsonObject + */ + public JsonObject toJson() { + return JsonUtils.buildJsonObject(this.detail.toJson()) // + .addProperty("type", this.type.name()) // + .addProperty("accessMode", this.accessMode.getAbbreviation()) // + .addProperty("text", this.text) // + .addProperty("unit", this.unit.getSymbol()) // + .addProperty("category", this.detail.getCategory().name()) // + .build(); + } + } + + private final String id; + private final String alias; private final String factoryId; private final TreeMap properties; + private final TreeMap channels; - public Component(String factoryId, TreeMap properties) { + public Component(String id, String alias, String factoryId, TreeMap properties, + TreeMap channels) { + this.id = id; + this.alias = alias; this.factoryId = factoryId; this.properties = properties; + this.channels = channels; + } + + public String getId() { + return id; + } + + public String getAlias() { + return alias; } public String getFactoryId() { @@ -41,29 +273,64 @@ public Map getProperties() { return properties; } + public Map getChannels() { + return channels; + } + + public Map getChannelsOfCategory(ChannelCategory channelCategory) { + return this.channels.entrySet().stream() + .filter(entry -> entry.getValue().getDetail().getCategory() == channelCategory) // + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + public Map getStateChannels() { + return this.getChannelsOfCategory(ChannelCategory.STATE); + } + /** * Returns the Component configuration as a JSON Object. * *
 		 * {
+		 *   alias: string,
 		 *   factoryId: string,
 		 *	 properties: {
 		 *     [key: string]: value
+		 *   },
+		 *   channels: {
+		 *     [channelId: string]: {}
 		 *   }
 		 * }
 		 * 
* * @return configuration as a JSON Object */ - public JsonObject toJson() { + public JsonObject toJson(JsonFormat jsonFormat) { JsonObject properties = new JsonObject(); for (Entry property : this.getProperties().entrySet()) { properties.add(property.getKey(), property.getValue()); } - return JsonUtils.buildJsonObject() // + JsonObjectBuilder result = JsonUtils.buildJsonObject() // + .addProperty("alias", this.getAlias()) // .addProperty("factoryId", this.getFactoryId()) // - .add("properties", properties) // - .build(); + .add("properties", properties); // + switch (jsonFormat) { + case WITHOUT_CHANNELS: + break; + + case COMPLETE: + JsonObject channels = new JsonObject(); + for (Entry channel : this.getChannels().entrySet()) { + channels.add(channel.getKey(), channel.getValue().toJson()); + } + result.add("channels", channels); // + break; + } + return result.build(); + } + + public enum JsonFormat { + COMPLETE, WITHOUT_CHANNELS; } /** @@ -73,26 +340,45 @@ public JsonObject toJson() { * @return the Component * @throws OpenemsNamedException on error */ - public static Component fromJson(JsonElement json) throws OpenemsNamedException { + public static Component fromJson(String componentId, JsonElement json) throws OpenemsNamedException { + String alias = JsonUtils.getAsOptionalString(json, "alias").orElse(componentId); + String factoryId = JsonUtils.getAsOptionalString(json, "factoryId").orElse("NO_FACTORY_ID"); TreeMap properties = new TreeMap<>(); - for (Entry entry : JsonUtils.getAsJsonObject(json, "properties").entrySet()) { - properties.put(entry.getKey(), entry.getValue()); + Optional jPropertiesOpt = JsonUtils.getAsOptionalJsonObject(json, "properties"); + if (jPropertiesOpt.isPresent()) { + for (Entry entry : jPropertiesOpt.get().entrySet()) { + properties.put(entry.getKey(), entry.getValue()); + } + } + TreeMap channels = new TreeMap<>(); + Optional jChannelsOpt = JsonUtils.getAsOptionalJsonObject(json, "channels"); + if (jChannelsOpt.isPresent()) { + for (Entry entry : jChannelsOpt.get().entrySet()) { + channels.put(entry.getKey(), Channel.fromJson(entry.getKey(), entry.getValue())); + } } return new Component(// - JsonUtils.getAsString(json, "factoryId"), // - properties); + componentId, // + alias, // + factoryId, // + properties, // + channels); } } + /** + * Represents an OpenEMS Component Factory. + */ public static class Factory { - public static Factory create(ObjectClassDefinition ocd, String[] natureIds) { + public static Factory create(String factoryId, ObjectClassDefinition ocd, String[] natureIds) { String name = ocd.getName(); String description = ocd.getDescription(); List properties = new ArrayList<>(); properties.addAll(Factory.toProperties(ocd, true)); properties.addAll(Factory.toProperties(ocd, false)); - return new Factory(name, description, properties.toArray(new Property[properties.size()]), natureIds); + return new Factory(factoryId, name, description, properties.toArray(new Property[properties.size()]), + natureIds); } private static List toProperties(ObjectClassDefinition ocd, boolean isRequired) { @@ -120,6 +406,9 @@ private static List toProperties(ObjectClassDefinition ocd, boolean is return properties; } + /** + * Represents a configuration option of an OpenEMS Component Factory. + */ public static class Property { private final String id; @@ -148,10 +437,26 @@ public static Property from(AttributeDefinition ad, boolean isRequired) { description = ""; } - JsonElement defaultValue = JsonUtils.getAsJsonElement(ad.getDefaultValue()); - if ((ad.getCardinality() == 0 || ad.getCardinality() == 1) && defaultValue.isJsonArray() - && ((JsonArray) defaultValue).size() == 1) { - defaultValue = ((JsonArray) defaultValue).get(0); + String[] defaultValues = ad.getDefaultValue(); + JsonElement defaultValue; + if (defaultValues == null) { + defaultValue = JsonNull.INSTANCE; + + } else if (ad.getCardinality() == 0) { + // Simple Type + if (defaultValues.length == 1) { + defaultValue = JsonUtils.getAsJsonElement(defaultValues[0]); + } else { + defaultValue = new JsonPrimitive(""); + } + + } else { + // Array Type + JsonArray defaultValueArray = new JsonArray(); + for (String value : defaultValues) { + defaultValueArray.add(JsonUtils.getAsJsonElement(value)); + } + defaultValue = defaultValueArray; } JsonObject schema; @@ -171,56 +476,62 @@ private static JsonObject getSchema(AttributeDefinition ad) { JsonObject schema = new JsonObject(); if (ad.getOptionLabels() != null && ad.getOptionValues() != null) { // use given options for schema - schema.addProperty("type", "select"); - JsonArray titleMap = new JsonArray(); + JsonArray options = new JsonArray(); for (int i = 0; i < ad.getOptionLabels().length; i++) { - titleMap.add(JsonUtils.buildJsonObject() // + String label = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, + ad.getOptionLabels()[i].replaceAll("_", " _")); + options.add(JsonUtils.buildJsonObject() // .addProperty("value", ad.getOptionValues()[i]) // - .addProperty("name", ad.getOptionLabels()[i]) // + .addProperty("label", label) // .build()); } - schema.add("titleMap", titleMap); + return JsonUtils.buildJsonObject() // + .addProperty("type", "select") // + .add("templateOptions", JsonUtils.buildJsonObject() // + .add("options", options) // + .build()) // + .build(); } else { // generate schema from AttributeDefinition Type switch (ad.getType()) { case AttributeDefinition.STRING: case AttributeDefinition.CHARACTER: - schema = JsonUtils.buildJsonObject() // + return JsonUtils.buildJsonObject() // .addProperty("type", "input") // .add("templateOptions", JsonUtils.buildJsonObject() // .addProperty("type", "text") // .build()) // .build(); - break; + case AttributeDefinition.LONG: case AttributeDefinition.INTEGER: case AttributeDefinition.SHORT: case AttributeDefinition.DOUBLE: case AttributeDefinition.FLOAT: case AttributeDefinition.BYTE: - schema = JsonUtils.buildJsonObject() // + return JsonUtils.buildJsonObject() // .addProperty("type", "input") // .add("templateOptions", JsonUtils.buildJsonObject() // .addProperty("type", "number") // .build()) // .build(); - break; + case AttributeDefinition.PASSWORD: - schema = JsonUtils.buildJsonObject() // + return JsonUtils.buildJsonObject() // .addProperty("type", "input") // .add("templateOptions", JsonUtils.buildJsonObject() // .addProperty("type", "password") // .build()) // .build(); - break; + case AttributeDefinition.BOOLEAN: - schema = JsonUtils.buildJsonObject() // + return JsonUtils.buildJsonObject() // .addProperty("type", "toggle") // .build(); - break; } } + return schema; } @@ -236,7 +547,8 @@ public static Property fromJson(JsonElement json) throws OpenemsNamedException { String name = JsonUtils.getAsString(json, "name"); String description = JsonUtils.getAsString(json, "description"); boolean isRequired = JsonUtils.getAsBoolean(json, "isRequired"); - JsonElement defaultValue = JsonUtils.getOptionalSubElement(json, "defaultValue").orElse(JsonNull.INSTANCE); + JsonElement defaultValue = JsonUtils.getOptionalSubElement(json, "defaultValue") + .orElse(JsonNull.INSTANCE); JsonObject schema = JsonUtils.getAsJsonObject(json, "schema"); return new Property(id, name, description, isRequired, defaultValue, schema); } @@ -272,18 +584,24 @@ public JsonObject toJson() { } + private final String id; private final String name; - private String description; - private Property[] properties; + private final String description; + private final Property[] properties; private final String[] natureIds; - public Factory(String name, String description, Property[] properties, String[] natureIds) { + public Factory(String id, String name, String description, Property[] properties, String[] natureIds) { + this.id = id; this.name = name; this.description = description; this.properties = properties; this.natureIds = natureIds; } + public String getId() { + return id; + } + public String getName() { return name; } @@ -335,17 +653,28 @@ public JsonObject toJson() { * @return the Factory * @throws OpenemsNamedException on error */ - public static Factory fromJson(JsonElement json) throws OpenemsNamedException { - String name = JsonUtils.getAsString(json, "name"); - String description = JsonUtils.getAsString(json, "description"); - String[] natureIds = JsonUtils.getAsStringArray(JsonUtils.getAsJsonArray(json, "natureIds")); - JsonArray jProperties = JsonUtils.getAsJsonArray(json, "properties"); - Property[] properties = new Property[jProperties.size()]; - for (int i = 0; i < jProperties.size(); i++) { - JsonElement jProperty = jProperties.get(i); - properties[i] = Property.fromJson(jProperty); - } - return new Factory(name, description, properties, natureIds); + public static Factory fromJson(String factoryId, JsonElement json) throws OpenemsNamedException { + // TODO Update to latest OpenEMS Edge! Remove "Optional" + String name = JsonUtils.getAsOptionalString(json, "name").orElse("Undefined"); + String description = JsonUtils.getAsOptionalString(json, "description").orElse(""); + Optional natureIdsOpt = JsonUtils.getAsOptionalJsonArray(json, "natureIds"); + if (!natureIdsOpt.isPresent()) { + natureIdsOpt = JsonUtils.getAsOptionalJsonArray(json, "natures"); + } + String[] natureIds = JsonUtils.getAsStringArray(natureIdsOpt.get()); + Optional jPropertiesOpt = JsonUtils.getAsOptionalJsonArray(json, "properties"); + Property[] properties; + if (jPropertiesOpt.isPresent()) { + JsonArray jProperties = jPropertiesOpt.get(); + properties = new Property[jProperties.size()]; + for (int i = 0; i < jProperties.size(); i++) { + JsonElement jProperty = jProperties.get(i); + properties[i] = Property.fromJson(jProperty); + } + } else { + properties = new Property[0]; + } + return new Factory(factoryId, name, description, properties, natureIds); } } @@ -445,7 +774,7 @@ public List getComponentsImplementingNature(String nature) { */ public JsonObject toJson() { return JsonUtils.buildJsonObject() // - .add("components", this.componentsToJson()) // + .add("components", this.componentsToJson(JsonFormat.COMPLETE)) // .add("factories", this.factoriesToJson()) // .build(); } @@ -461,10 +790,10 @@ public JsonObject toJson() { * * @return Components as a JSON Object */ - public JsonObject componentsToJson() { + public JsonObject componentsToJson(JsonFormat jsonFormat) { JsonObject components = new JsonObject(); for (Entry entry : this.getComponents().entrySet()) { - components.add(entry.getKey(), entry.getValue().toJson()); + components.add(entry.getKey(), entry.getValue().toJson(jsonFormat)); } return components; } @@ -504,11 +833,11 @@ public static EdgeConfig fromJson(JsonObject json) throws OpenemsNamedException } for (Entry entry : JsonUtils.getAsJsonObject(json, "components").entrySet()) { - result.addComponent(entry.getKey(), Component.fromJson(entry.getValue())); + result.addComponent(entry.getKey(), Component.fromJson(entry.getKey(), entry.getValue())); } for (Entry entry : JsonUtils.getAsJsonObject(json, "factories").entrySet()) { - result.addFactory(entry.getKey(), Factory.fromJson(entry.getValue())); + result.addFactory(entry.getKey(), Factory.fromJson(entry.getKey(), entry.getValue())); } return result; @@ -522,6 +851,7 @@ private static EdgeConfig fromOldJsonFormat(JsonObject json) throws OpenemsNamed for (Entry entry : things.entrySet()) { JsonObject config = JsonUtils.getAsJsonObject(entry.getValue()); String id = JsonUtils.getAsString(config, "id"); + String alias = JsonUtils.getAsOptionalString(config, "alias").orElse(id); String clazz = JsonUtils.getAsString(config, "class"); TreeMap properties = new TreeMap<>(); for (Entry property : config.entrySet()) { @@ -538,7 +868,8 @@ private static EdgeConfig fromOldJsonFormat(JsonObject json) throws OpenemsNamed } } } - result.addComponent(id, new EdgeConfig.Component(clazz, properties)); + TreeMap channels = new TreeMap<>(); + result.addComponent(id, new EdgeConfig.Component(id, alias, clazz, properties, channels)); } JsonObject metas = JsonUtils.getAsJsonObject(json, "meta"); @@ -546,8 +877,8 @@ private static EdgeConfig fromOldJsonFormat(JsonObject json) throws OpenemsNamed JsonObject meta = JsonUtils.getAsJsonObject(entry.getValue()); String id = JsonUtils.getAsString(meta, "class"); String[] implement = JsonUtils.getAsStringArray(JsonUtils.getAsJsonArray(meta, "implements")); - Property[] properties = new Property[0]; - result.addFactory(id, new EdgeConfig.Factory(id, "", properties, implement)); + Factory.Property[] properties = new Factory.Property[0]; + result.addFactory(id, new EdgeConfig.Factory(id, id, "", properties, implement)); } return result; diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/OptionsEnum.java b/io.openems.common/src/io/openems/common/types/OptionsEnum.java similarity index 93% rename from io.openems.edge.common/src/io/openems/edge/common/channel/OptionsEnum.java rename to io.openems.common/src/io/openems/common/types/OptionsEnum.java index 77e66a08449..7661e37e31c 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/OptionsEnum.java +++ b/io.openems.common/src/io/openems/common/types/OptionsEnum.java @@ -1,4 +1,4 @@ -package io.openems.edge.common.channel; +package io.openems.common.types; public interface OptionsEnum { diff --git a/io.openems.common/src/io/openems/common/utils/JsonUtils.java b/io.openems.common/src/io/openems/common/utils/JsonUtils.java index d9acb4047f1..6e5e4cf1966 100644 --- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -48,6 +48,48 @@ public static boolean getAsBoolean(JsonElement jElement, String memberName) thro return jPrimitive.getAsBoolean(); } + public static Optional getAsOptionalBoolean(JsonElement element, String memberName) { + try { + return Optional.of(getAsBoolean(element, memberName)); + } catch (OpenemsNamedException e) { + return Optional.empty(); + } + } + + public static > E getAsEnum(Class enumType, JsonElement jElement, String memberName) + throws OpenemsNamedException { + String element = getAsString(jElement, memberName); + try { + return (E) Enum.valueOf(enumType, element); + } catch (IllegalArgumentException e) { + throw OpenemsError.JSON_NO_ENUM_MEMBER.exception(memberName, element); + } + } + + public static > Optional getAsOptionalEnum(Class enumType, JsonElement jElement, + String memberName) { + Optional elementOpt = getAsOptionalString(jElement, memberName); + if (!elementOpt.isPresent()) { + return Optional.empty(); + } + try { + return Optional.ofNullable((E) Enum.valueOf(enumType, elementOpt.get())); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } + + public static int getAsInt(JsonElement jElement) throws OpenemsNamedException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsInt(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Integer.parseInt(string); + } + throw OpenemsError.JSON_NO_INTEGER.exception(jPrimitive.toString().replaceAll("%", "%%")); + } + public static int getAsInt(JsonElement jElement, String memberName) throws OpenemsNamedException { JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); if (jPrimitive.isNumber()) { @@ -208,6 +250,14 @@ public static long getAsLong(JsonElement jElement, String memberName) throws Ope throw OpenemsError.JSON_NO_NUMBER.exception(jPrimitive.toString().replaceAll("%", "%%")); } + public static Optional getAsOptionalInt(JsonElement jElement) { + try { + return Optional.of(getAsInt(jElement)); + } catch (OpenemsNamedException e) { + return Optional.empty(); + } + } + public static Optional getAsOptionalInt(JsonElement jElement, String memberName) { try { return Optional.of(getAsInt(jElement, memberName)); diff --git a/io.openems.common/src/io/openems/common/worker/AbstractImmediateWorker.java b/io.openems.common/src/io/openems/common/worker/AbstractImmediateWorker.java index 99b15886bcb..a74cba83ca5 100644 --- a/io.openems.common/src/io/openems/common/worker/AbstractImmediateWorker.java +++ b/io.openems.common/src/io/openems/common/worker/AbstractImmediateWorker.java @@ -25,5 +25,5 @@ protected final int getCycleTime() { } @Override - protected abstract void forever(); + protected abstract void forever() throws InterruptedException; } diff --git a/io.openems.common/src/io/openems/common/worker/AbstractWorker.java b/io.openems.common/src/io/openems/common/worker/AbstractWorker.java index 69b581eafcc..66084e2e784 100644 --- a/io.openems.common/src/io/openems/common/worker/AbstractWorker.java +++ b/io.openems.common/src/io/openems/common/worker/AbstractWorker.java @@ -89,7 +89,7 @@ public void run() { /* * Wait for next cycle */ - int cycleTime = getCycleTime(); + int cycleTime = AbstractWorker.this.getCycleTime(); if (cycleTime == DO_NOT_WAIT) { // no wait } else if (cycleTime > 0) { @@ -109,7 +109,7 @@ public void run() { /* * Call forever() forever. */ - forever(); + AbstractWorker.this.forever(); // Everything went ok -> reset onWorkerExceptionSleep onWorkerExceptionSleep = 1; diff --git a/io.openems.edge.application/EdgeApp.bndrun b/io.openems.edge.application/EdgeApp.bndrun index 5e1294c46f5..8f706418f2d 100644 --- a/io.openems.edge.application/EdgeApp.bndrun +++ b/io.openems.edge.application/EdgeApp.bndrun @@ -36,10 +36,12 @@ bnd.identity;id='io.openems.edge.controller.debug.detailedlog',\ bnd.identity;id='io.openems.edge.controller.debug.log',\ bnd.identity;id='io.openems.edge.controller.dischargelimitconsideringcellvoltage',\ + bnd.identity;id='io.openems.edge.controller.ess.acisland',\ bnd.identity;id='io.openems.edge.controller.ess.limittotaldischarge',\ bnd.identity;id='io.openems.edge.controller.ess.onefullcycle',\ bnd.identity;id='io.openems.edge.controller.evcs',\ bnd.identity;id='io.openems.edge.controller.highloadtimeslot',\ + bnd.identity;id='io.openems.edge.controller.io.alarm',\ bnd.identity;id='io.openems.edge.controller.io.fixdigitaloutput',\ bnd.identity;id='io.openems.edge.controller.symmetric.balancing',\ bnd.identity;id='io.openems.edge.controller.symmetric.balancingschedule',\ @@ -51,6 +53,7 @@ bnd.identity;id='io.openems.edge.controller.symmetric.randompower',\ bnd.identity;id='io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic',\ bnd.identity;id='io.openems.edge.core',\ + bnd.identity;id='io.openems.edge.ess.byd.container',\ bnd.identity;id='io.openems.edge.ess.cluster',\ bnd.identity;id='io.openems.edge.ess.core',\ bnd.identity;id='io.openems.edge.ess.fenecon.commercial40',\ @@ -67,11 +70,13 @@ bnd.identity;id='io.openems.edge.io.kmtronic',\ bnd.identity;id='io.openems.edge.io.wago',\ bnd.identity;id='io.openems.edge.kostal.piko',\ + bnd.identity;id='io.openems.edge.meter.artemes.am2',\ bnd.identity;id='io.openems.edge.meter.bcontrol.em300',\ bnd.identity;id='io.openems.edge.meter.carlo.gavazzi.em300',\ bnd.identity;id='io.openems.edge.meter.janitza.umg96rme',\ bnd.identity;id='io.openems.edge.meter.microcare.sdm630',\ bnd.identity;id='io.openems.edge.meter.socomec',\ + bnd.identity;id='io.openems.edge.meter.virtual',\ bnd.identity;id='io.openems.edge.meter.weidmueller',\ bnd.identity;id='io.openems.edge.pvinverter.solarlog',\ bnd.identity;id='io.openems.edge.scheduler.allalphabetically',\ @@ -82,18 +87,13 @@ com.google.gson;version='[2.8.5,2.8.6)',\ com.google.guava.failureaccess;version='[1.0.1,1.0.2)',\ io.openems.common;version=snapshot,\ - io.openems.edge.application;version=snapshot,\ io.openems.edge.battery.soltaro;version=snapshot,\ io.openems.edge.bridge.modbus;version=snapshot,\ io.openems.edge.common;version=snapshot,\ - io.openems.edge.controller.api.backend;version=snapshot,\ io.openems.edge.controller.api.core;version=snapshot,\ - io.openems.edge.controller.api.rest;version=snapshot,\ io.openems.edge.core;version=snapshot,\ io.openems.edge.ess.core;version=snapshot,\ - io.openems.edge.ess.kaco.blueplanet.gridsave50;version=snapshot,\ io.openems.edge.scheduler.fixedorder;version=snapshot,\ - io.openems.edge.simulator;version=snapshot,\ io.openems.edge.timedata.influxdb;version=snapshot,\ io.openems.shared.influxdb;version=snapshot,\ io.openems.wrapper.influxdb-java;version=snapshot,\ @@ -116,21 +116,36 @@ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ org.osgi.service.event;version='[1.3.1,1.3.2)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + io.openems.edge.controller.chp.soc;version=snapshot,\ + io.openems.edge.pvinverter.solarlog;version=snapshot,\ + org.jsr-305;version='[3.0.2,3.0.3)',\ + com.fazecast.jSerialComm;version='[2.2.2,2.2.3)',\ + com.ghgande.j2mod;version='[2.5.2,2.5.3)',\ + com.google.guava;version='[27.1.0,27.1.1)',\ + io.openems.edge.application;version=snapshot,\ + io.openems.edge.controller.api.backend;version=snapshot,\ io.openems.edge.controller.api.modbus;version=snapshot,\ + io.openems.edge.controller.api.rest;version=snapshot,\ io.openems.edge.controller.api.websocket;version=snapshot,\ + io.openems.edge.ess.fenecon.commercial40;version=snapshot,\ + io.openems.edge.ess.mr.gridcon;version=snapshot,\ + io.openems.edge.ess.sinexcel;version=snapshot,\ + io.openems.edge.evcs.keba.kecontact;version=snapshot,\ + io.openems.edge.kostal.piko;version=snapshot,\ io.openems.edge.controller.asymmetric.balancingcosphi;version=snapshot,\ io.openems.edge.controller.asymmetric.fixactivepower;version=snapshot,\ io.openems.edge.controller.asymmetric.fixreactivepower;version=snapshot,\ io.openems.edge.controller.asymmetric.phaserectification;version=snapshot,\ io.openems.edge.controller.channelthreshold;version=snapshot,\ - io.openems.edge.controller.chp.soc;version=snapshot,\ io.openems.edge.controller.debug.detailedlog;version=snapshot,\ io.openems.edge.controller.debug.log;version=snapshot,\ io.openems.edge.controller.dischargelimitconsideringcellvoltage;version=snapshot,\ + io.openems.edge.controller.ess.acisland;version=snapshot,\ io.openems.edge.controller.ess.limittotaldischarge;version=snapshot,\ io.openems.edge.controller.ess.onefullcycle;version=snapshot,\ io.openems.edge.controller.evcs;version=snapshot,\ io.openems.edge.controller.highloadtimeslot;version=snapshot,\ + io.openems.edge.controller.io.alarm;version=snapshot,\ io.openems.edge.controller.io.fixdigitaloutput;version=snapshot,\ io.openems.edge.controller.symmetric.balancing;version=snapshot,\ io.openems.edge.controller.symmetric.balancingschedule;version=snapshot,\ @@ -140,30 +155,27 @@ io.openems.edge.controller.symmetric.linearpowerband;version=snapshot,\ io.openems.edge.controller.symmetric.peakshaving;version=snapshot,\ io.openems.edge.controller.symmetric.randompower;version=snapshot,\ + io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic;version=snapshot,\ io.openems.edge.ess.cluster;version=snapshot,\ - io.openems.edge.ess.fenecon.commercial40;version=snapshot,\ - io.openems.edge.ess.mr.gridcon;version=snapshot,\ + io.openems.edge.ess.kaco.blueplanet.gridsave50;version=snapshot,\ io.openems.edge.ess.refu;version=snapshot,\ - io.openems.edge.ess.sinexcel;version=snapshot,\ - io.openems.edge.evcs.keba.kecontact;version=snapshot,\ - io.openems.edge.meter.weidmueller;version=snapshot,\ + io.openems.edge.ess.sma;version=snapshot,\ io.openems.edge.ess.streetscooter;version=snapshot,\ io.openems.edge.fenecon.dess;version=snapshot,\ io.openems.edge.fenecon.mini;version=snapshot,\ io.openems.edge.fenecon.pro;version=snapshot,\ io.openems.edge.io.kmtronic;version=snapshot,\ io.openems.edge.io.wago;version=snapshot,\ - io.openems.edge.kostal.piko;version=snapshot,\ io.openems.edge.meter.bcontrol.em300;version=snapshot,\ io.openems.edge.meter.carlo.gavazzi.em300;version=snapshot,\ io.openems.edge.meter.janitza.umg96rme;version=snapshot,\ io.openems.edge.meter.microcare.sdm630;version=snapshot,\ io.openems.edge.meter.socomec;version=snapshot,\ - io.openems.edge.pvinverter.solarlog;version=snapshot,\ + io.openems.edge.meter.weidmueller;version=snapshot,\ io.openems.edge.scheduler.allalphabetically;version=snapshot,\ - io.openems.edge.ess.sma;version=snapshot,\ - io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic;version=snapshot,\ - org.jsr-305;version='[3.0.2,3.0.3)',\ - com.fazecast.jSerialComm;version='[2.2.2,2.2.3)',\ - com.ghgande.j2mod;version='[2.5.2,2.5.3)',\ - com.google.guava;version='[27.1.0,27.1.1)' \ No newline at end of file + io.openems.edge.simulator;version=snapshot,\ + io.openems.edge.meter.virtual;version=snapshot,\ + io.openems.edge.meter.artemes.am2;version=snapshot,\ + io.openems.edge.ess.byd.container;version=snapshot,\ + io.openems.wrapper.fastexcel;version=snapshot,\ + io.openems.wrapper.opczip;version=snapshot \ No newline at end of file diff --git a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java b/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java index a99c19a0bfd..cf0a9b93341 100644 --- a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java +++ b/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java @@ -2,10 +2,11 @@ import org.osgi.annotation.versioning.ProviderType; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; import io.openems.edge.common.modbusslave.ModbusType; @@ -68,7 +69,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: mA * */ - CURRENT(Doc.of(OpenemsType.INTEGER).unit(Unit.MILLIAMPERE)), + CURRENT(Doc.of(OpenemsType.INTEGER).unit(Unit.AMPERE)), /** * Capacity of battery. @@ -180,8 +181,8 @@ public Doc doc() { } } - public static ModbusSlaveNatureTable getModbusSlaveNatureTable() { - return ModbusSlaveNatureTable.of(Battery.class, 100) // + public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) { + return ModbusSlaveNatureTable.of(Battery.class, accessMode, 100) // .channel(0, ChannelId.SOC, ModbusType.UINT16) // .channel(1, ChannelId.SOH, ModbusType.UINT16) // .channel(2, ChannelId.VOLTAGE, ModbusType.FLOAT32) // diff --git a/io.openems.edge.battery.api/src/io/openems/edge/battery/test/DummyBattery.java b/io.openems.edge.battery.api/src/io/openems/edge/battery/test/DummyBattery.java index 96a485ef465..a381b0e5739 100644 --- a/io.openems.edge.battery.api/src/io/openems/edge/battery/test/DummyBattery.java +++ b/io.openems.edge.battery.api/src/io/openems/edge/battery/test/DummyBattery.java @@ -21,7 +21,7 @@ public DummyBattery(String id) { for (Channel channel : this.channels()) { channel.nextProcessImage(); } - super.activate(null, id, true); + super.activate(null, id, "", true); } } diff --git a/io.openems.edge.battery.soltaro/bnd.bnd b/io.openems.edge.battery.soltaro/bnd.bnd index 67736f2681b..9011e5cff67 100644 --- a/io.openems.edge.battery.soltaro/bnd.bnd +++ b/io.openems.edge.battery.soltaro/bnd.bnd @@ -5,9 +5,10 @@ Bundle-Version: 1.0.0.${tstamp} Export-Package: \ io.openems.edge.battery.api,\ io.openems.edge.battery.soltaro,\ - io.openems.edge.battery.soltaro.master,\ - io.openems.edge.battery.soltaro.versionb,\ - io.openems.edge.battery.soltaro.multirack + io.openems.edge.battery.soltaro.cluster.versiona,\ + io.openems.edge.battery.soltaro.single.versionb,\ + io.openems.edge.battery.soltaro.single.versiona,\ + io.openems.edge.battery.soltaro.cluster.versionb -includeresource: {readme.md} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryState.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryState.java index c26dd75477f..317d99b10c3 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryState.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryState.java @@ -5,4 +5,6 @@ public enum BatteryState { DEFAULT, ON, OFF, + CONFIGURE + ; } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ChannelIdImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ChannelIdImpl.java similarity index 88% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ChannelIdImpl.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ChannelIdImpl.java index 03f40bc2705..1f768b322c4 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ChannelIdImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ChannelIdImpl.java @@ -1,4 +1,4 @@ -package io.openems.edge.battery.soltaro.versionb; +package io.openems.edge.battery.soltaro; import io.openems.edge.common.channel.ChannelId; import io.openems.edge.common.channel.Doc; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ModuleParameters.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ModuleParameters.java similarity index 95% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ModuleParameters.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ModuleParameters.java index c1c1923a546..9afca778980 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ModuleParameters.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ModuleParameters.java @@ -1,4 +1,4 @@ -package io.openems.edge.battery.soltaro.versionb; +package io.openems.edge.battery.soltaro; public enum ModuleParameters { diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/State.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/State.java similarity index 85% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/State.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/State.java index 6ce54816582..a53b7f18465 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/State.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/State.java @@ -1,9 +1,8 @@ -package io.openems.edge.battery.soltaro.master; +package io.openems.edge.battery.soltaro; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum State implements OptionsEnum { - UNDEFINED("Undefined", -1), // PENDING("Pending", 0), // OFF("Off", 1), // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Master.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Cluster.java similarity index 54% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Master.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Cluster.java index 78356b691bb..faff1314765 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Master.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Cluster.java @@ -1,4 +1,4 @@ -package io.openems.edge.battery.soltaro.master; +package io.openems.edge.battery.soltaro.cluster.versiona; import java.time.LocalDateTime; import java.util.ArrayList; @@ -22,15 +22,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.battery.api.Battery; import io.openems.edge.battery.soltaro.BatteryState; -import io.openems.edge.battery.soltaro.master.Enums.RackUsage; -import io.openems.edge.battery.soltaro.master.Enums.StartStop; +import io.openems.edge.battery.soltaro.State; +import io.openems.edge.battery.soltaro.cluster.versiona.Enums.RackUsage; +import io.openems.edge.battery.soltaro.cluster.versiona.Enums.StartStop; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; @@ -45,12 +47,12 @@ @Designate(ocd = Config.class, factory = true) @Component( // - name = "Bms.Fenecon.SoltaroMaster", // + name = "Bms.Soltaro.Cluster.VersionA", // immediate = true, // configurationPolicy = ConfigurationPolicy.REQUIRE, // property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // ) -public class Master extends AbstractOpenemsModbusComponent implements Battery, OpenemsComponent, EventHandler { +public class Cluster extends AbstractOpenemsModbusComponent implements Battery, OpenemsComponent, EventHandler { public static final int DISCHARGE_MIN_V = 696; public static final int CHARGE_MAX_V = 854; @@ -58,7 +60,7 @@ public class Master extends AbstractOpenemsModbusComponent implements Battery, O public static final int CHARGE_MAX_A = 0; public static final Integer CAPACITY_KWH = 150; - private final Logger log = LoggerFactory.getLogger(Master.class); + private final Logger log = LoggerFactory.getLogger(Cluster.class); private String modbusBridgeId; private BatteryState batteryState; @Reference @@ -66,17 +68,17 @@ public class Master extends AbstractOpenemsModbusComponent implements Battery, O private State state = State.UNDEFINED; private Config config; - public Master() { + public Cluster() { super(// OpenemsComponent.ChannelId.values(), // Battery.ChannelId.values(), // - MasterChannelId.values() // + ClusterChannelId.values() // ); - this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(Master.CHARGE_MAX_A); - this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE).setNextValue(Master.CHARGE_MAX_V); - this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(Master.DISCHARGE_MAX_A); - this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE).setNextValue(Master.DISCHARGE_MIN_V); - this.channel(Battery.ChannelId.CAPACITY).setNextValue(Master.CAPACITY_KWH); + this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(Cluster.CHARGE_MAX_A); + this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE).setNextValue(Cluster.CHARGE_MAX_V); + this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(Cluster.DISCHARGE_MAX_A); + this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE).setNextValue(Cluster.DISCHARGE_MIN_V); + this.channel(Battery.ChannelId.CAPACITY).setNextValue(Cluster.CAPACITY_KWH); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) @@ -86,9 +88,9 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { - log.info("Master.activate()"); + log.info("Cluster.activate()"); this.config = config; - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); this.modbusBridgeId = config.modbus_id(); @@ -104,9 +106,9 @@ private void recalcMaxCurrent() { if (config.rack1IsUsed() && !config.rack2IsUsed() && !config.rack3IsUsed()) { // Only rack 1 is configured --> use max current of rack 1 Optional chargeMaxCurrentOpt = (Optional) this - .channel(MasterChannelId.RACK_1_MAX_CHARGE_CURRENT).value().asOptional(); + .channel(ClusterChannelId.RACK_1_MAX_CHARGE_CURRENT).value().asOptional(); Optional dischargeMaxCurrentOpt = (Optional) this - .channel(MasterChannelId.RACK_1_MAX_DISCHARGE_CURRENT).value().asOptional(); + .channel(ClusterChannelId.RACK_1_MAX_DISCHARGE_CURRENT).value().asOptional(); if (chargeMaxCurrentOpt.isPresent()) { chargeMaxCurrent = chargeMaxCurrentOpt.get(); } @@ -116,9 +118,9 @@ private void recalcMaxCurrent() { } else if (!config.rack1IsUsed() && config.rack2IsUsed() && !config.rack3IsUsed()) { // Only rack 2 is configured --> use max current of rack 2 Optional chargeMaxCurrentOpt = (Optional) this - .channel(MasterChannelId.RACK_2_MAX_CHARGE_CURRENT).value().asOptional(); + .channel(ClusterChannelId.RACK_2_MAX_CHARGE_CURRENT).value().asOptional(); Optional dischargeMaxCurrentOpt = (Optional) this - .channel(MasterChannelId.RACK_2_MAX_DISCHARGE_CURRENT).value().asOptional(); + .channel(ClusterChannelId.RACK_2_MAX_DISCHARGE_CURRENT).value().asOptional(); if (chargeMaxCurrentOpt.isPresent()) { chargeMaxCurrent = chargeMaxCurrentOpt.get(); } @@ -128,9 +130,9 @@ private void recalcMaxCurrent() { } else if (!config.rack1IsUsed() && !config.rack2IsUsed() && config.rack3IsUsed()) { // Only rack 3 is configured --> use max current of rack 3 Optional chargeMaxCurrentOpt = (Optional) this - .channel(MasterChannelId.RACK_3_MAX_CHARGE_CURRENT).value().asOptional(); + .channel(ClusterChannelId.RACK_3_MAX_CHARGE_CURRENT).value().asOptional(); Optional dischargeMaxCurrentOpt = (Optional) this - .channel(MasterChannelId.RACK_3_MAX_DISCHARGE_CURRENT).value().asOptional(); + .channel(ClusterChannelId.RACK_3_MAX_DISCHARGE_CURRENT).value().asOptional(); if (chargeMaxCurrentOpt.isPresent()) { chargeMaxCurrent = chargeMaxCurrentOpt.get(); } @@ -139,10 +141,10 @@ private void recalcMaxCurrent() { } } else { // more than one rack is configured, use information of cluster - Optional chargeMaxCurrentOpt = (Optional) this.channel(MasterChannelId.CHARGE_MAX_CURRENT) + Optional chargeMaxCurrentOpt = (Optional) this.channel(ClusterChannelId.CHARGE_MAX_CURRENT_CLUSTER) .value().asOptional(); Optional dischargeMaxCurrentOpt = (Optional) this - .channel(MasterChannelId.DISCHARGE_MAX_CURRENT).value().asOptional(); + .channel(ClusterChannelId.DISCHARGE_MAX_CURRENT_CLUSTER).value().asOptional(); if (chargeMaxCurrentOpt.isPresent()) { chargeMaxCurrent = chargeMaxCurrentOpt.get(); } @@ -183,6 +185,9 @@ private void handleBatteryState() { case ON: startSystem(); break; + case CONFIGURE: + System.out.println("Cluster cannot be configured currently!"); + break; } } @@ -194,7 +199,7 @@ private void handleBatteryState() { private LocalDateTime startAttemptTime = null; private void handleStateMachine() { - log.info("Master.doNormalHandling(): State: " + this.getStateMachineState()); + log.info("Cluster.doNormalHandling(): State: " + this.getStateMachineState()); boolean readyForWorking = false; switch (this.getStateMachineState()) { case ERROR: @@ -272,73 +277,73 @@ private void handleStateMachine() { } private boolean isError() { - if (readValueFromStateChannel(MasterChannelId.MASTER_ALARM_PCS_OUT_OF_CONTROL)) + if (readValueFromStateChannel(ClusterChannelId.MASTER_ALARM_PCS_OUT_OF_CONTROL)) return true; if (config.rack1IsUsed()) { - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_CHA_CURRENT_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_CHA_CURRENT_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)) return true; } if (config.rack2IsUsed()) { - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_CHA_CURRENT_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_CHA_CURRENT_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)) return true; } if (config.rack3IsUsed()) { - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_CHA_CURRENT_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_CHA_CURRENT_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH)) return true; - if (readValueFromStateChannel(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)) + if (readValueFromStateChannel(ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)) return true; } return false; @@ -355,9 +360,9 @@ private boolean isSystemStopped() { // racks are configured, the other ones // must also be stopped, otherwise status is not clear - IntegerReadChannel rack1StateChannel = this.channel(MasterChannelId.RACK_1_STATE); - IntegerReadChannel rack2StateChannel = this.channel(MasterChannelId.RACK_2_STATE); - IntegerReadChannel rack3StateChannel = this.channel(MasterChannelId.RACK_3_STATE); + IntegerReadChannel rack1StateChannel = this.channel(ClusterChannelId.RACK_1_STATE); + IntegerReadChannel rack2StateChannel = this.channel(ClusterChannelId.RACK_2_STATE); + IntegerReadChannel rack3StateChannel = this.channel(ClusterChannelId.RACK_3_STATE); Optional v1 = rack1StateChannel.value().asOptional(); Optional v2 = rack2StateChannel.value().asOptional(); @@ -375,9 +380,9 @@ private boolean isSystemRunning() { // stopped, otherwise // status is not definitively clear - IntegerReadChannel rack1StateChannel = this.channel(MasterChannelId.RACK_1_STATE); - IntegerReadChannel rack2StateChannel = this.channel(MasterChannelId.RACK_2_STATE); - IntegerReadChannel rack3StateChannel = this.channel(MasterChannelId.RACK_3_STATE); + IntegerReadChannel rack1StateChannel = this.channel(ClusterChannelId.RACK_1_STATE); + IntegerReadChannel rack2StateChannel = this.channel(ClusterChannelId.RACK_2_STATE); + IntegerReadChannel rack3StateChannel = this.channel(ClusterChannelId.RACK_3_STATE); Optional val1Opt = rack1StateChannel.value().asOptional(); Optional val2Opt = rack2StateChannel.value().asOptional(); @@ -423,19 +428,19 @@ private boolean isSystemRunning() { private boolean isSystemStatePending() { boolean ret = true; if (ret && config.rack1IsUsed()) { - IntegerReadChannel rack1StateChannel = this.channel(MasterChannelId.RACK_1_STATE); + IntegerReadChannel rack1StateChannel = this.channel(ClusterChannelId.RACK_1_STATE); Optional val = rack1StateChannel.value().asOptional(); ret = ret && val.isPresent(); } if (ret && config.rack2IsUsed()) { - IntegerReadChannel rack2StateChannel = this.channel(MasterChannelId.RACK_2_STATE); + IntegerReadChannel rack2StateChannel = this.channel(ClusterChannelId.RACK_2_STATE); Optional val = rack2StateChannel.value().asOptional(); ret = ret && val.isPresent(); } if (ret && config.rack3IsUsed()) { - IntegerReadChannel rack3StateChannel = this.channel(MasterChannelId.RACK_3_STATE); + IntegerReadChannel rack3StateChannel = this.channel(ClusterChannelId.RACK_3_STATE); Optional val = rack3StateChannel.value().asOptional(); ret = ret && val.isPresent(); } @@ -451,11 +456,11 @@ public String debugLog() { } private void startSystem() { - IntegerWriteChannel rack1UsageChannel = this.channel(MasterChannelId.RACK_1_USAGE); - IntegerWriteChannel rack2UsageChannel = this.channel(MasterChannelId.RACK_2_USAGE); - IntegerWriteChannel rack3UsageChannel = this.channel(MasterChannelId.RACK_3_USAGE); + IntegerWriteChannel rack1UsageChannel = this.channel(ClusterChannelId.RACK_1_USAGE); + IntegerWriteChannel rack2UsageChannel = this.channel(ClusterChannelId.RACK_2_USAGE); + IntegerWriteChannel rack3UsageChannel = this.channel(ClusterChannelId.RACK_3_USAGE); - IntegerWriteChannel startStopChannel = this.channel(MasterChannelId.START_STOP); + IntegerWriteChannel startStopChannel = this.channel(ClusterChannelId.START_STOP); try { startStopChannel.setNextWriteValue(StartStop.START.getValue()); @@ -474,23 +479,23 @@ private void startSystem() { } else { rack3UsageChannel.setNextWriteValue(RackUsage.UNUSED.getValue()); } - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { log.error("Error while trying to start system\n" + e.getMessage()); } } private void stopSystem() { - IntegerWriteChannel startStopChannel = this.channel(MasterChannelId.START_STOP); - IntegerWriteChannel rack1UsageChannel = this.channel(MasterChannelId.RACK_1_USAGE); - IntegerWriteChannel rack2UsageChannel = this.channel(MasterChannelId.RACK_2_USAGE); - IntegerWriteChannel rack3UsageChannel = this.channel(MasterChannelId.RACK_3_USAGE); + IntegerWriteChannel startStopChannel = this.channel(ClusterChannelId.START_STOP); + IntegerWriteChannel rack1UsageChannel = this.channel(ClusterChannelId.RACK_1_USAGE); + IntegerWriteChannel rack2UsageChannel = this.channel(ClusterChannelId.RACK_2_USAGE); + IntegerWriteChannel rack3UsageChannel = this.channel(ClusterChannelId.RACK_3_USAGE); try { startStopChannel.setNextWriteValue(StartStop.STOP.getValue()); rack1UsageChannel.setNextWriteValue(RackUsage.UNUSED.getValue()); rack2UsageChannel.setNextWriteValue(RackUsage.UNUSED.getValue()); rack3UsageChannel.setNextWriteValue(RackUsage.UNUSED.getValue()); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { log.error("Error while trying to stop system\n" + e.getMessage()); } } @@ -505,7 +510,7 @@ public State getStateMachineState() { public void setStateMachineState(State state) { this.state = state; - this.channel(MasterChannelId.STATE_MACHINE).setNextValue(this.state); + this.channel(ClusterChannelId.STATE_MACHINE).setNextValue(this.state); } private static final int BASE_ADDRESS_RACK_1 = 0x2000; @@ -517,1327 +522,1327 @@ protected ModbusProtocol defineModbusProtocol() { Collection tasks = new ArrayList<>(); tasks.addAll(Arrays.asList(new Task[] { // -------- Registers of master -------------------------------------- - new FC16WriteRegistersTask(0x1017, m(MasterChannelId.START_STOP, new UnsignedWordElement(0x1017)), // - m(MasterChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // - m(MasterChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // - m(MasterChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)) // + new FC16WriteRegistersTask(0x1017, m(ClusterChannelId.START_STOP, new UnsignedWordElement(0x1017)), // + m(ClusterChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // + m(ClusterChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // + m(ClusterChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)) // ), // new FC3ReadRegistersTask(0x1017, Priority.HIGH, - m(MasterChannelId.START_STOP, new UnsignedWordElement(0x1017)), // - m(MasterChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // - m(MasterChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // - m(MasterChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)) // + m(ClusterChannelId.START_STOP, new UnsignedWordElement(0x1017)), // + m(ClusterChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // + m(ClusterChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // + m(ClusterChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)) // ), // new FC3ReadRegistersTask(0x1044, Priority.LOW, // - m(MasterChannelId.CHARGE_INDICATION, new UnsignedWordElement(0x1044)), // - m(MasterChannelId.CURRENT, new UnsignedWordElement(0x1045), // + m(ClusterChannelId.CHARGE_INDICATION, new UnsignedWordElement(0x1044)), // + m(Battery.ChannelId.CURRENT, new UnsignedWordElement(0x1045), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // new DummyRegisterElement(0x1046, 0x1047), // // m(Battery.ChannelId.SOC, new UnsignedWordElement(0x1047)), // SoC is not calculated correctly - m(MasterChannelId.SYSTEM_RUNNING_STATE, new UnsignedWordElement(0x1048)), // - m(MasterChannelId.VOLTAGE, new UnsignedWordElement(0x1049), // + m(ClusterChannelId.SYSTEM_RUNNING_STATE, new UnsignedWordElement(0x1048)), // + m(Battery.ChannelId.VOLTAGE, new UnsignedWordElement(0x1049), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC3ReadRegistersTask(0x104D, Priority.HIGH, // - m(MasterChannelId.CHARGE_MAX_CURRENT, new UnsignedWordElement(0x104D), + m(ClusterChannelId.CHARGE_MAX_CURRENT_CLUSTER, new UnsignedWordElement(0x104D), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.DISCHARGE_MAX_CURRENT, new UnsignedWordElement(0x104E), + m(ClusterChannelId.DISCHARGE_MAX_CURRENT_CLUSTER, new UnsignedWordElement(0x104E), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC3ReadRegistersTask(0x1081, Priority.LOW, // - bm(new UnsignedWordElement(0x1081)) // - .m(MasterChannelId.MASTER_ALARM_PCS_OUT_OF_CONTROL, 1) // - .m(MasterChannelId.MASTER_ALARM_PCS_COMMUNICATION_FAULT, 0) // - .build(), // - bm(new UnsignedWordElement(0x1082)) // - .m(MasterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_1, 0) // - .m(MasterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_2, 1) // - .m(MasterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_3, 2) // - .build() // + m(new BitsWordElement(0x1081, this) // + .bit(1, ClusterChannelId.MASTER_ALARM_PCS_OUT_OF_CONTROL) // + .bit(0, ClusterChannelId.MASTER_ALARM_PCS_COMMUNICATION_FAULT) // + ), // + m(new BitsWordElement(0x1082, this) // + .bit(0, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_1) // + .bit(1, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_2) // + .bit(2, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_3) // + ) // ) // })); // ---------------- registers of rack 1 ----------------------------- tasks.addAll(Arrays.asList(new Task[] { new FC16WriteRegistersTask(BASE_ADDRESS_RACK_1 + 0x1, // - m(MasterChannelId.RACK_1_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x1)) // + m(ClusterChannelId.RACK_1_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x1)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x1, Priority.HIGH, // - m(MasterChannelId.RACK_1_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x1)) // + m(ClusterChannelId.RACK_1_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x1)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x100, Priority.LOW, // - m(MasterChannelId.RACK_1_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x100), // + m(ClusterChannelId.RACK_1_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x100), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_1_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x101), // + m(ClusterChannelId.RACK_1_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x101), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_1_CHARGE_INDICATION, + m(ClusterChannelId.RACK_1_CHARGE_INDICATION, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x102)), // - m(MasterChannelId.RACK_1_SOC, + m(ClusterChannelId.RACK_1_SOC, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x103).onUpdateCallback(val -> { recalculateSoc(); })), // - m(MasterChannelId.RACK_1_SOH, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x104)), // - m(MasterChannelId.RACK_1_MAX_CELL_VOLTAGE_ID, + m(ClusterChannelId.RACK_1_SOH, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x104)), // + m(ClusterChannelId.RACK_1_MAX_CELL_VOLTAGE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x105)), // - m(MasterChannelId.RACK_1_MAX_CELL_VOLTAGE, + m(ClusterChannelId.RACK_1_MAX_CELL_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x106)), // - m(MasterChannelId.RACK_1_MIN_CELL_VOLTAGE_ID, + m(ClusterChannelId.RACK_1_MIN_CELL_VOLTAGE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x107)), // - m(MasterChannelId.RACK_1_MIN_CELL_VOLTAGE, + m(ClusterChannelId.RACK_1_MIN_CELL_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x108)), // - m(MasterChannelId.RACK_1_MAX_CELL_TEMPERATURE_ID, + m(ClusterChannelId.RACK_1_MAX_CELL_TEMPERATURE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x109)), // - m(MasterChannelId.RACK_1_MAX_CELL_TEMPERATURE, + m(ClusterChannelId.RACK_1_MAX_CELL_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x10A)), // - m(MasterChannelId.RACK_1_MIN_CELL_TEMPERATURE_ID, + m(ClusterChannelId.RACK_1_MIN_CELL_TEMPERATURE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x10B)), // - m(MasterChannelId.RACK_1_MIN_CELL_TEMPERATURE, + m(ClusterChannelId.RACK_1_MIN_CELL_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x10C)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x140, Priority.LOW, // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x140)) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH, 0) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH, 1) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_CHA_CURRENT_HIGH, 2) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_LOW, 3) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW, 4) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH, 5) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH, 6) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW, 7) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH, 14) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x141)) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH, 0) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH, 1) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CHA_CURRENT_HIGH, 2) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_VOLTAGE_LOW, 3) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW, 4) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH, 5) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH, 6) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW, 7) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_SOC_LOW, 8) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH, 9) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH, 11) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_INSULATION_LOW, 12) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH, 13) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH, 14) // - .m(MasterChannelId.RACK_1_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - m(MasterChannelId.RACK_1_RUN_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x142)) // + m(new BitsWordElement(BASE_ADDRESS_RACK_1 + 0x140, this) // + .bit(0, ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH) // + .bit(1, ClusterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH) // + .bit(2, ClusterChannelId.RACK_1_ALARM_LEVEL_2_CHA_CURRENT_HIGH) // + .bit(3, ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_VOLTAGE_LOW) // + .bit(4, ClusterChannelId.RACK_1_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW) // + .bit(5, ClusterChannelId.RACK_1_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH) // + .bit(6, ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH) // + .bit(7, ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW) // + .bit(14, ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH) // + .bit(15, ClusterChannelId.RACK_1_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW) // + ), // + m(new BitsWordElement(BASE_ADDRESS_RACK_1 + 0x141, this) // + .bit(0, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH) // + .bit(1, ClusterChannelId.RACK_1_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH) // + .bit(2, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CHA_CURRENT_HIGH) // + .bit(3, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_VOLTAGE_LOW) // + .bit(4, ClusterChannelId.RACK_1_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW) // + .bit(5, ClusterChannelId.RACK_1_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH) // + .bit(6, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH) // + .bit(7, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW) // + .bit(8, ClusterChannelId.RACK_1_ALARM_LEVEL_1_SOC_LOW) // + .bit(9, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH) // + .bit(11, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH) // + .bit(12, ClusterChannelId.RACK_1_ALARM_LEVEL_1_INSULATION_LOW) // + .bit(13, ClusterChannelId.RACK_1_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH) // + .bit(14, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH) // + .bit(15, ClusterChannelId.RACK_1_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW) // + ), // + m(ClusterChannelId.RACK_1_RUN_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x142)) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x160, Priority.HIGH, // - m(MasterChannelId.RACK_1_MAX_CHARGE_CURRENT, + m(ClusterChannelId.RACK_1_MAX_CHARGE_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x160), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_1_MAX_DISCHARGE_CURRENT, + m(ClusterChannelId.RACK_1_MAX_DISCHARGE_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x161), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x185, Priority.LOW, // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x185)) // - .m(MasterChannelId.RACK_1_FAILURE_SAMPLING_WIRE, 0)// - .m(MasterChannelId.RACK_1_FAILURE_CONNECTOR_WIRE, 1)// - .m(MasterChannelId.RACK_1_FAILURE_LTC6803, 2)// - .m(MasterChannelId.RACK_1_FAILURE_VOLTAGE_SAMPLING, 3)// - .m(MasterChannelId.RACK_1_FAILURE_TEMP_SAMPLING, 4)// - .m(MasterChannelId.RACK_1_FAILURE_TEMP_SENSOR, 5)// - .m(MasterChannelId.RACK_1_FAILURE_BALANCING_MODULE, 8)// - .m(MasterChannelId.RACK_1_FAILURE_TEMP_SAMPLING_LINE, 9)// - .m(MasterChannelId.RACK_1_FAILURE_INTRANET_COMMUNICATION, 10)// - .m(MasterChannelId.RACK_1_FAILURE_EEPROM, 11)// - .m(MasterChannelId.RACK_1_FAILURE_INITIALIZATION, 12)// - .build() // + m(new BitsWordElement(BASE_ADDRESS_RACK_1 + 0x185, this) // + .bit(0, ClusterChannelId.RACK_1_FAILURE_SAMPLING_WIRE)// + .bit(1, ClusterChannelId.RACK_1_FAILURE_CONNECTOR_WIRE)// + .bit(2, ClusterChannelId.RACK_1_FAILURE_LTC6803)// + .bit(3, ClusterChannelId.RACK_1_FAILURE_VOLTAGE_SAMPLING)// + .bit(4, ClusterChannelId.RACK_1_FAILURE_TEMP_SAMPLING)// + .bit(5, ClusterChannelId.RACK_1_FAILURE_TEMP_SENSOR)// + .bit(8, ClusterChannelId.RACK_1_FAILURE_BALANCING_MODULE)// + .bit(9, ClusterChannelId.RACK_1_FAILURE_TEMP_SAMPLING_LINE)// + .bit(10, ClusterChannelId.RACK_1_FAILURE_INTRANET_COMMUNICATION)// + .bit(11, ClusterChannelId.RACK_1_FAILURE_EEPROM)// + .bit(12, ClusterChannelId.RACK_1_FAILURE_INITIALIZATION)// + ) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x800, Priority.LOW, // - m(MasterChannelId.RACK_1_BATTERY_000_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_000_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x800)), // - m(MasterChannelId.RACK_1_BATTERY_001_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_001_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x801)), // - m(MasterChannelId.RACK_1_BATTERY_002_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_002_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x802)), // - m(MasterChannelId.RACK_1_BATTERY_003_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_003_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x803)), // - m(MasterChannelId.RACK_1_BATTERY_004_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_004_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x804)), // - m(MasterChannelId.RACK_1_BATTERY_005_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_005_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x805)), // - m(MasterChannelId.RACK_1_BATTERY_006_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_006_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x806)), // - m(MasterChannelId.RACK_1_BATTERY_007_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_007_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x807)), // - m(MasterChannelId.RACK_1_BATTERY_008_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_008_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x808)), // - m(MasterChannelId.RACK_1_BATTERY_009_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_009_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x809)), // - m(MasterChannelId.RACK_1_BATTERY_010_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_010_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x80A)), // - m(MasterChannelId.RACK_1_BATTERY_011_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_011_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x80B)), // - m(MasterChannelId.RACK_1_BATTERY_012_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_012_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x80C)), // - m(MasterChannelId.RACK_1_BATTERY_013_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_013_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x80D)), // - m(MasterChannelId.RACK_1_BATTERY_014_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_014_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x80E)), // - m(MasterChannelId.RACK_1_BATTERY_015_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_015_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x80F)), // - m(MasterChannelId.RACK_1_BATTERY_016_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_016_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x810)), // - m(MasterChannelId.RACK_1_BATTERY_017_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_017_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x811)), // - m(MasterChannelId.RACK_1_BATTERY_018_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_018_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x812)), // - m(MasterChannelId.RACK_1_BATTERY_019_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_019_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x813)), // - m(MasterChannelId.RACK_1_BATTERY_020_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_020_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x814)), // - m(MasterChannelId.RACK_1_BATTERY_021_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_021_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x815)), // - m(MasterChannelId.RACK_1_BATTERY_022_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_022_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x816)), // - m(MasterChannelId.RACK_1_BATTERY_023_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_023_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x817)), // - m(MasterChannelId.RACK_1_BATTERY_024_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_024_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x818)), // - m(MasterChannelId.RACK_1_BATTERY_025_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_025_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x819)), // - m(MasterChannelId.RACK_1_BATTERY_026_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_026_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x81A)), // - m(MasterChannelId.RACK_1_BATTERY_027_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_027_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x81B)), // - m(MasterChannelId.RACK_1_BATTERY_028_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_028_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x81C)), // - m(MasterChannelId.RACK_1_BATTERY_029_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_029_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x81D)), // - m(MasterChannelId.RACK_1_BATTERY_030_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_030_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x81E)), // - m(MasterChannelId.RACK_1_BATTERY_031_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_031_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x81F)), // - m(MasterChannelId.RACK_1_BATTERY_032_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_032_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x820)), // - m(MasterChannelId.RACK_1_BATTERY_033_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_033_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x821)), // - m(MasterChannelId.RACK_1_BATTERY_034_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_034_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x822)), // - m(MasterChannelId.RACK_1_BATTERY_035_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_035_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x823)), // - m(MasterChannelId.RACK_1_BATTERY_036_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_036_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x824)), // - m(MasterChannelId.RACK_1_BATTERY_037_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_037_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x825)), // - m(MasterChannelId.RACK_1_BATTERY_038_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_038_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x826)), // - m(MasterChannelId.RACK_1_BATTERY_039_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_039_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x827)), // - m(MasterChannelId.RACK_1_BATTERY_040_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_040_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x828)), // - m(MasterChannelId.RACK_1_BATTERY_041_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_041_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x829)), // - m(MasterChannelId.RACK_1_BATTERY_042_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_042_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x82A)), // - m(MasterChannelId.RACK_1_BATTERY_043_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_043_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x82B)), // - m(MasterChannelId.RACK_1_BATTERY_044_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_044_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x82C)), // - m(MasterChannelId.RACK_1_BATTERY_045_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_045_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x82D)), // - m(MasterChannelId.RACK_1_BATTERY_046_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_046_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x82E)), // - m(MasterChannelId.RACK_1_BATTERY_047_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_047_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x82F)), // - m(MasterChannelId.RACK_1_BATTERY_048_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_048_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x830)), // - m(MasterChannelId.RACK_1_BATTERY_049_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_049_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x831)), // - m(MasterChannelId.RACK_1_BATTERY_050_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_050_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x832)), // - m(MasterChannelId.RACK_1_BATTERY_051_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_051_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x833)), // - m(MasterChannelId.RACK_1_BATTERY_052_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_052_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x834)), // - m(MasterChannelId.RACK_1_BATTERY_053_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_053_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x835)), // - m(MasterChannelId.RACK_1_BATTERY_054_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_054_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x836)), // - m(MasterChannelId.RACK_1_BATTERY_055_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_055_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x837)), // - m(MasterChannelId.RACK_1_BATTERY_056_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_056_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x838)), // - m(MasterChannelId.RACK_1_BATTERY_057_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_057_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x839)), // - m(MasterChannelId.RACK_1_BATTERY_058_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_058_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x83A)), // - m(MasterChannelId.RACK_1_BATTERY_059_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_059_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x83B)), // - m(MasterChannelId.RACK_1_BATTERY_060_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_060_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x83C)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x83D, Priority.LOW, // - m(MasterChannelId.RACK_1_BATTERY_061_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_061_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x83D)), // - m(MasterChannelId.RACK_1_BATTERY_062_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_062_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x83E)), // - m(MasterChannelId.RACK_1_BATTERY_063_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_063_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x83F)), // - m(MasterChannelId.RACK_1_BATTERY_064_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_064_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x840)), // - m(MasterChannelId.RACK_1_BATTERY_065_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_065_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x841)), // - m(MasterChannelId.RACK_1_BATTERY_066_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_066_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x842)), // - m(MasterChannelId.RACK_1_BATTERY_067_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_067_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x843)), // - m(MasterChannelId.RACK_1_BATTERY_068_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_068_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x844)), // - m(MasterChannelId.RACK_1_BATTERY_069_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_069_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x845)), // - m(MasterChannelId.RACK_1_BATTERY_070_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_070_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x846)), // - m(MasterChannelId.RACK_1_BATTERY_071_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_071_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x847)), // - m(MasterChannelId.RACK_1_BATTERY_072_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_072_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x848)), // - m(MasterChannelId.RACK_1_BATTERY_073_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_073_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x849)), // - m(MasterChannelId.RACK_1_BATTERY_074_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_074_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x84A)), // - m(MasterChannelId.RACK_1_BATTERY_075_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_075_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x84B)), // - m(MasterChannelId.RACK_1_BATTERY_076_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_076_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x84C)), // - m(MasterChannelId.RACK_1_BATTERY_077_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_077_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x84D)), // - m(MasterChannelId.RACK_1_BATTERY_078_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_078_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x84E)), // - m(MasterChannelId.RACK_1_BATTERY_079_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_079_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x84F)), // - m(MasterChannelId.RACK_1_BATTERY_080_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_080_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x850)), // - m(MasterChannelId.RACK_1_BATTERY_081_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_081_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x851)), // - m(MasterChannelId.RACK_1_BATTERY_082_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_082_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x852)), // - m(MasterChannelId.RACK_1_BATTERY_083_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_083_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x853)), // - m(MasterChannelId.RACK_1_BATTERY_084_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_084_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x854)), // - m(MasterChannelId.RACK_1_BATTERY_085_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_085_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x855)), // - m(MasterChannelId.RACK_1_BATTERY_086_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_086_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x856)), // - m(MasterChannelId.RACK_1_BATTERY_087_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_087_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x857)), // - m(MasterChannelId.RACK_1_BATTERY_088_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_088_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x858)), // - m(MasterChannelId.RACK_1_BATTERY_089_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_089_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x859)), // - m(MasterChannelId.RACK_1_BATTERY_090_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_090_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x85A)), // - m(MasterChannelId.RACK_1_BATTERY_091_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_091_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x85B)), // - m(MasterChannelId.RACK_1_BATTERY_092_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_092_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x85C)), // - m(MasterChannelId.RACK_1_BATTERY_093_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_093_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x85D)), // - m(MasterChannelId.RACK_1_BATTERY_094_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_094_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x85E)), // - m(MasterChannelId.RACK_1_BATTERY_095_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_095_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x85F)), // - m(MasterChannelId.RACK_1_BATTERY_096_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_096_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x860)), // - m(MasterChannelId.RACK_1_BATTERY_097_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_097_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x861)), // - m(MasterChannelId.RACK_1_BATTERY_098_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_098_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x862)), // - m(MasterChannelId.RACK_1_BATTERY_099_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_099_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x863)), // - m(MasterChannelId.RACK_1_BATTERY_100_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_100_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x864)), // - m(MasterChannelId.RACK_1_BATTERY_101_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_101_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x865)), // - m(MasterChannelId.RACK_1_BATTERY_102_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_102_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x866)), // - m(MasterChannelId.RACK_1_BATTERY_103_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_103_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x867)), // - m(MasterChannelId.RACK_1_BATTERY_104_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_104_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x868)), // - m(MasterChannelId.RACK_1_BATTERY_105_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_105_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x869)), // - m(MasterChannelId.RACK_1_BATTERY_106_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_106_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x86A)), // - m(MasterChannelId.RACK_1_BATTERY_107_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_107_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x86B)), // - m(MasterChannelId.RACK_1_BATTERY_108_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_108_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x86C)), // - m(MasterChannelId.RACK_1_BATTERY_109_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_109_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x86D)), // - m(MasterChannelId.RACK_1_BATTERY_110_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_110_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x86E)), // - m(MasterChannelId.RACK_1_BATTERY_111_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_111_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x86F)), // - m(MasterChannelId.RACK_1_BATTERY_112_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_112_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x870)), // - m(MasterChannelId.RACK_1_BATTERY_113_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_113_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x871)), // - m(MasterChannelId.RACK_1_BATTERY_114_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_114_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x872)), // - m(MasterChannelId.RACK_1_BATTERY_115_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_115_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x873)), // - m(MasterChannelId.RACK_1_BATTERY_116_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_116_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x874)), // - m(MasterChannelId.RACK_1_BATTERY_117_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_117_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x875)), // - m(MasterChannelId.RACK_1_BATTERY_118_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_118_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x876)), // - m(MasterChannelId.RACK_1_BATTERY_119_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_119_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x877)) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x878, Priority.LOW, // - m(MasterChannelId.RACK_1_BATTERY_120_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_120_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x878)), // - m(MasterChannelId.RACK_1_BATTERY_121_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_121_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x879)), // - m(MasterChannelId.RACK_1_BATTERY_122_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_122_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x87A)), // - m(MasterChannelId.RACK_1_BATTERY_123_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_123_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x87B)), // - m(MasterChannelId.RACK_1_BATTERY_124_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_124_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x87C)), // - m(MasterChannelId.RACK_1_BATTERY_125_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_125_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x87D)), // - m(MasterChannelId.RACK_1_BATTERY_126_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_126_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x87E)), // - m(MasterChannelId.RACK_1_BATTERY_127_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_127_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x87F)), // - m(MasterChannelId.RACK_1_BATTERY_128_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_128_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x880)), // - m(MasterChannelId.RACK_1_BATTERY_129_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_129_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x881)), // - m(MasterChannelId.RACK_1_BATTERY_130_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_130_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x882)), // - m(MasterChannelId.RACK_1_BATTERY_131_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_131_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x883)), // - m(MasterChannelId.RACK_1_BATTERY_132_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_132_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x884)), // - m(MasterChannelId.RACK_1_BATTERY_133_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_133_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x885)), // - m(MasterChannelId.RACK_1_BATTERY_134_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_134_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x886)), // - m(MasterChannelId.RACK_1_BATTERY_135_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_135_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x887)), // - m(MasterChannelId.RACK_1_BATTERY_136_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_136_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x888)), // - m(MasterChannelId.RACK_1_BATTERY_137_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_137_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x889)), // - m(MasterChannelId.RACK_1_BATTERY_138_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_138_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x88A)), // - m(MasterChannelId.RACK_1_BATTERY_139_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_139_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x88B)), // - m(MasterChannelId.RACK_1_BATTERY_140_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_140_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x88C)), // - m(MasterChannelId.RACK_1_BATTERY_141_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_141_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x88D)), // - m(MasterChannelId.RACK_1_BATTERY_142_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_142_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x88E)), // - m(MasterChannelId.RACK_1_BATTERY_143_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_143_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x88F)), // - m(MasterChannelId.RACK_1_BATTERY_144_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_144_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x890)), // - m(MasterChannelId.RACK_1_BATTERY_145_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_145_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x891)), // - m(MasterChannelId.RACK_1_BATTERY_146_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_146_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x892)), // - m(MasterChannelId.RACK_1_BATTERY_147_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_147_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x893)), // - m(MasterChannelId.RACK_1_BATTERY_148_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_148_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x894)), // - m(MasterChannelId.RACK_1_BATTERY_149_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_149_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x895)), // - m(MasterChannelId.RACK_1_BATTERY_150_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_150_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x896)), // - m(MasterChannelId.RACK_1_BATTERY_151_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_151_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x897)), // - m(MasterChannelId.RACK_1_BATTERY_152_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_152_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x898)), // - m(MasterChannelId.RACK_1_BATTERY_153_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_153_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x899)), // - m(MasterChannelId.RACK_1_BATTERY_154_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_154_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x89A)), // - m(MasterChannelId.RACK_1_BATTERY_155_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_155_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x89B)), // - m(MasterChannelId.RACK_1_BATTERY_156_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_156_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x89C)), // - m(MasterChannelId.RACK_1_BATTERY_157_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_157_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x89D)), // - m(MasterChannelId.RACK_1_BATTERY_158_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_158_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x89E)), // - m(MasterChannelId.RACK_1_BATTERY_159_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_159_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x89F)), // - m(MasterChannelId.RACK_1_BATTERY_160_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_160_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A0)), // - m(MasterChannelId.RACK_1_BATTERY_161_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_161_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A1)), // - m(MasterChannelId.RACK_1_BATTERY_162_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_162_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A2)), // - m(MasterChannelId.RACK_1_BATTERY_163_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_163_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A3)), // - m(MasterChannelId.RACK_1_BATTERY_164_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_164_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A4)), // - m(MasterChannelId.RACK_1_BATTERY_165_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_165_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A5)), // - m(MasterChannelId.RACK_1_BATTERY_166_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_166_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A6)), // - m(MasterChannelId.RACK_1_BATTERY_167_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_167_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A7)), // - m(MasterChannelId.RACK_1_BATTERY_168_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_168_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A8)), // - m(MasterChannelId.RACK_1_BATTERY_169_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_169_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8A9)), // - m(MasterChannelId.RACK_1_BATTERY_170_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_170_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8AA)), // - m(MasterChannelId.RACK_1_BATTERY_171_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_171_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8AB)), // - m(MasterChannelId.RACK_1_BATTERY_172_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_172_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8AC)), // - m(MasterChannelId.RACK_1_BATTERY_173_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_173_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8AD)), // - m(MasterChannelId.RACK_1_BATTERY_174_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_174_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8AE)), // - m(MasterChannelId.RACK_1_BATTERY_175_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_175_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8AF)), // - m(MasterChannelId.RACK_1_BATTERY_176_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_176_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B0)), // - m(MasterChannelId.RACK_1_BATTERY_177_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_177_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B1)), // - m(MasterChannelId.RACK_1_BATTERY_178_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_178_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B2)), // - m(MasterChannelId.RACK_1_BATTERY_179_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_179_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B3)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0x8B4, Priority.LOW, // - m(MasterChannelId.RACK_1_BATTERY_180_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_180_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B4)), // - m(MasterChannelId.RACK_1_BATTERY_181_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_181_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B5)), // - m(MasterChannelId.RACK_1_BATTERY_182_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_182_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B6)), // - m(MasterChannelId.RACK_1_BATTERY_183_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_183_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B7)), // - m(MasterChannelId.RACK_1_BATTERY_184_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_184_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B8)), // - m(MasterChannelId.RACK_1_BATTERY_185_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_185_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8B9)), // - m(MasterChannelId.RACK_1_BATTERY_186_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_186_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8BA)), // - m(MasterChannelId.RACK_1_BATTERY_187_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_187_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8BB)), // - m(MasterChannelId.RACK_1_BATTERY_188_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_188_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8BC)), // - m(MasterChannelId.RACK_1_BATTERY_189_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_189_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8BD)), // - m(MasterChannelId.RACK_1_BATTERY_190_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_190_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8BE)), // - m(MasterChannelId.RACK_1_BATTERY_191_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_191_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8BF)), // - m(MasterChannelId.RACK_1_BATTERY_192_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_192_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C0)), // - m(MasterChannelId.RACK_1_BATTERY_193_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_193_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C1)), // - m(MasterChannelId.RACK_1_BATTERY_194_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_194_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C2)), // - m(MasterChannelId.RACK_1_BATTERY_195_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_195_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C3)), // - m(MasterChannelId.RACK_1_BATTERY_196_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_196_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C4)), // - m(MasterChannelId.RACK_1_BATTERY_197_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_197_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C5)), // - m(MasterChannelId.RACK_1_BATTERY_198_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_198_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C6)), // - m(MasterChannelId.RACK_1_BATTERY_199_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_199_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C7)), // - m(MasterChannelId.RACK_1_BATTERY_200_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_200_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C8)), // - m(MasterChannelId.RACK_1_BATTERY_201_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_201_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8C9)), // - m(MasterChannelId.RACK_1_BATTERY_202_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_202_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8CA)), // - m(MasterChannelId.RACK_1_BATTERY_203_VOLTAGE, + m(ClusterChannelId.RACK_1_BATTERY_203_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0x8CB)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0xC00, Priority.LOW, // - m(MasterChannelId.RACK_1_BATTERY_000_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_000_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC00)), // - m(MasterChannelId.RACK_1_BATTERY_001_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_001_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC01)), // - m(MasterChannelId.RACK_1_BATTERY_002_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_002_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC02)), // - m(MasterChannelId.RACK_1_BATTERY_003_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_003_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC03)), // - m(MasterChannelId.RACK_1_BATTERY_004_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_004_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC04)), // - m(MasterChannelId.RACK_1_BATTERY_005_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_005_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC05)), // - m(MasterChannelId.RACK_1_BATTERY_006_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_006_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC06)), // - m(MasterChannelId.RACK_1_BATTERY_007_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_007_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC07)), // - m(MasterChannelId.RACK_1_BATTERY_008_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_008_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC08)), // - m(MasterChannelId.RACK_1_BATTERY_009_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_009_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC09)), // - m(MasterChannelId.RACK_1_BATTERY_010_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_010_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC0A)), // - m(MasterChannelId.RACK_1_BATTERY_011_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_011_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC0B)), // - m(MasterChannelId.RACK_1_BATTERY_012_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_012_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC0C)), // - m(MasterChannelId.RACK_1_BATTERY_013_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_013_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC0D)), // - m(MasterChannelId.RACK_1_BATTERY_014_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_014_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC0E)), // - m(MasterChannelId.RACK_1_BATTERY_015_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_015_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC0F)), // - m(MasterChannelId.RACK_1_BATTERY_016_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_016_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC10)), // - m(MasterChannelId.RACK_1_BATTERY_017_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_017_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC11)), // - m(MasterChannelId.RACK_1_BATTERY_018_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_018_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC12)), // - m(MasterChannelId.RACK_1_BATTERY_019_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_019_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC13)), // - m(MasterChannelId.RACK_1_BATTERY_020_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_020_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC14)), // - m(MasterChannelId.RACK_1_BATTERY_021_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_021_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC15)), // - m(MasterChannelId.RACK_1_BATTERY_022_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_022_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC16)), // - m(MasterChannelId.RACK_1_BATTERY_023_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_023_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC17)), // - m(MasterChannelId.RACK_1_BATTERY_024_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_024_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC18)), // - m(MasterChannelId.RACK_1_BATTERY_025_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_025_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC19)), // - m(MasterChannelId.RACK_1_BATTERY_026_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_026_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC1A)), // - m(MasterChannelId.RACK_1_BATTERY_027_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_027_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC1B)), // - m(MasterChannelId.RACK_1_BATTERY_028_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_028_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC1C)), // - m(MasterChannelId.RACK_1_BATTERY_029_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_029_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC1D)), // - m(MasterChannelId.RACK_1_BATTERY_030_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_030_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC1E)), // - m(MasterChannelId.RACK_1_BATTERY_031_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_031_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC1F)), // - m(MasterChannelId.RACK_1_BATTERY_032_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_032_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC20)), // - m(MasterChannelId.RACK_1_BATTERY_033_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_033_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC21)), // - m(MasterChannelId.RACK_1_BATTERY_034_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_034_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC22)), // - m(MasterChannelId.RACK_1_BATTERY_035_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_035_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC23)), // - m(MasterChannelId.RACK_1_BATTERY_036_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_036_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC24)), // - m(MasterChannelId.RACK_1_BATTERY_037_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_037_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC25)), // - m(MasterChannelId.RACK_1_BATTERY_038_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_038_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC26)), // - m(MasterChannelId.RACK_1_BATTERY_039_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_039_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC27)), // - m(MasterChannelId.RACK_1_BATTERY_040_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_040_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC28)), // - m(MasterChannelId.RACK_1_BATTERY_041_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_041_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC29)), // - m(MasterChannelId.RACK_1_BATTERY_042_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_042_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC2A)), // - m(MasterChannelId.RACK_1_BATTERY_043_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_043_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC2B)), // - m(MasterChannelId.RACK_1_BATTERY_044_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_044_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC2C)), // - m(MasterChannelId.RACK_1_BATTERY_045_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_045_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC2D)), // - m(MasterChannelId.RACK_1_BATTERY_046_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_046_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC2E)), // - m(MasterChannelId.RACK_1_BATTERY_047_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_047_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC2F)), // - m(MasterChannelId.RACK_1_BATTERY_048_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_048_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC30)), // - m(MasterChannelId.RACK_1_BATTERY_049_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_049_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC31)), // - m(MasterChannelId.RACK_1_BATTERY_050_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_050_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC32)), // - m(MasterChannelId.RACK_1_BATTERY_051_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_051_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC33)), // - m(MasterChannelId.RACK_1_BATTERY_052_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_052_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC34)), // - m(MasterChannelId.RACK_1_BATTERY_053_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_053_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC35)), // - m(MasterChannelId.RACK_1_BATTERY_054_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_054_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC36)), // - m(MasterChannelId.RACK_1_BATTERY_055_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_055_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC37)), // - m(MasterChannelId.RACK_1_BATTERY_056_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_056_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC38)), // - m(MasterChannelId.RACK_1_BATTERY_057_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_057_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC39)), // - m(MasterChannelId.RACK_1_BATTERY_058_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_058_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC3A)), // - m(MasterChannelId.RACK_1_BATTERY_059_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_059_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC3B)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_1 + 0xC3C, Priority.LOW, // - m(MasterChannelId.RACK_1_BATTERY_060_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_060_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC3C)), // - m(MasterChannelId.RACK_1_BATTERY_061_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_061_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC3D)), // - m(MasterChannelId.RACK_1_BATTERY_062_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_062_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC3E)), // - m(MasterChannelId.RACK_1_BATTERY_063_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_063_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC3F)), // - m(MasterChannelId.RACK_1_BATTERY_064_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_064_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC40)), // - m(MasterChannelId.RACK_1_BATTERY_065_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_065_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC41)), // - m(MasterChannelId.RACK_1_BATTERY_066_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_066_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC42)), // - m(MasterChannelId.RACK_1_BATTERY_067_TEMPERATURE, + m(ClusterChannelId.RACK_1_BATTERY_067_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_1 + 0xC43)) // ) // })); // ---------------- registers of rack 2 ----------------------------- tasks.addAll(Arrays.asList(new Task[] { new FC16WriteRegistersTask(BASE_ADDRESS_RACK_2 + 0x1, // - m(MasterChannelId.RACK_2_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x1)) // + m(ClusterChannelId.RACK_2_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x1)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x1, Priority.HIGH, // - m(MasterChannelId.RACK_2_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x1)) // + m(ClusterChannelId.RACK_2_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x1)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x100, Priority.LOW, // - m(MasterChannelId.RACK_2_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x100), // + m(ClusterChannelId.RACK_2_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x100), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_2_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x101), // + m(ClusterChannelId.RACK_2_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x101), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_2_CHARGE_INDICATION, + m(ClusterChannelId.RACK_2_CHARGE_INDICATION, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x102)), // - m(MasterChannelId.RACK_2_SOC, + m(ClusterChannelId.RACK_2_SOC, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x103).onUpdateCallback(val -> { recalculateSoc(); })), // - m(MasterChannelId.RACK_2_SOH, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x104)), // - m(MasterChannelId.RACK_2_MAX_CELL_VOLTAGE_ID, + m(ClusterChannelId.RACK_2_SOH, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x104)), // + m(ClusterChannelId.RACK_2_MAX_CELL_VOLTAGE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x105)), // - m(MasterChannelId.RACK_2_MAX_CELL_VOLTAGE, + m(ClusterChannelId.RACK_2_MAX_CELL_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x106)), // - m(MasterChannelId.RACK_2_MIN_CELL_VOLTAGE_ID, + m(ClusterChannelId.RACK_2_MIN_CELL_VOLTAGE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x107)), // - m(MasterChannelId.RACK_2_MIN_CELL_VOLTAGE, + m(ClusterChannelId.RACK_2_MIN_CELL_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x108)), // - m(MasterChannelId.RACK_2_MAX_CELL_TEMPERATURE_ID, + m(ClusterChannelId.RACK_2_MAX_CELL_TEMPERATURE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x109)), // - m(MasterChannelId.RACK_2_MAX_CELL_TEMPERATURE, + m(ClusterChannelId.RACK_2_MAX_CELL_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x10A)), // - m(MasterChannelId.RACK_2_MIN_CELL_TEMPERATURE_ID, + m(ClusterChannelId.RACK_2_MIN_CELL_TEMPERATURE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x10B)), // - m(MasterChannelId.RACK_2_MIN_CELL_TEMPERATURE, + m(ClusterChannelId.RACK_2_MIN_CELL_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x10C)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x140, Priority.LOW, // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x140)) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH, 0) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH, 1) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_CHA_CURRENT_HIGH, 2) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_LOW, 3) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW, 4) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH, 5) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH, 6) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW, 7) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH, 14) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x141)) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH, 0) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH, 1) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CHA_CURRENT_HIGH, 2) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_VOLTAGE_LOW, 3) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW, 4) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH, 5) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH, 6) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW, 7) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_SOC_LOW, 8) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH, 9) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH, 11) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_INSULATION_LOW, 12) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH, 13) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH, 14) // - .m(MasterChannelId.RACK_2_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - m(MasterChannelId.RACK_2_RUN_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x142)) // + m(new BitsWordElement(BASE_ADDRESS_RACK_2 + 0x140, this) // + .bit(0, ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH) // + .bit(1, ClusterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH) // + .bit(2, ClusterChannelId.RACK_2_ALARM_LEVEL_2_CHA_CURRENT_HIGH) // + .bit(3, ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_VOLTAGE_LOW) // + .bit(4, ClusterChannelId.RACK_2_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW) // + .bit(5, ClusterChannelId.RACK_2_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH) // + .bit(6, ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH) // + .bit(7, ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW) // + .bit(14, ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH) // + .bit(15, ClusterChannelId.RACK_2_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW) // + ), // + m(new BitsWordElement(BASE_ADDRESS_RACK_2 + 0x141, this) // + .bit(0, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH) // + .bit(1, ClusterChannelId.RACK_2_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH) // + .bit(2, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CHA_CURRENT_HIGH) // + .bit(3, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_VOLTAGE_LOW) // + .bit(4, ClusterChannelId.RACK_2_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW) // + .bit(5, ClusterChannelId.RACK_2_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH) // + .bit(6, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH) // + .bit(7, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW) // + .bit(8, ClusterChannelId.RACK_2_ALARM_LEVEL_1_SOC_LOW) // + .bit(9, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH) // + .bit(11, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH) // + .bit(12, ClusterChannelId.RACK_2_ALARM_LEVEL_1_INSULATION_LOW) // + .bit(13, ClusterChannelId.RACK_2_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH) // + .bit(14, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH) // + .bit(15, ClusterChannelId.RACK_2_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW) // + ), // + m(ClusterChannelId.RACK_2_RUN_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x142)) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x160, Priority.HIGH, // - m(MasterChannelId.RACK_2_MAX_CHARGE_CURRENT, + m(ClusterChannelId.RACK_2_MAX_CHARGE_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x160), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_2_MAX_DISCHARGE_CURRENT, + m(ClusterChannelId.RACK_2_MAX_DISCHARGE_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x161), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x185, Priority.LOW, // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x185)) // - .m(MasterChannelId.RACK_2_FAILURE_SAMPLING_WIRE, 0)// - .m(MasterChannelId.RACK_2_FAILURE_CONNECTOR_WIRE, 1)// - .m(MasterChannelId.RACK_2_FAILURE_LTC6803, 2)// - .m(MasterChannelId.RACK_2_FAILURE_VOLTAGE_SAMPLING, 3)// - .m(MasterChannelId.RACK_2_FAILURE_TEMP_SAMPLING, 4)// - .m(MasterChannelId.RACK_2_FAILURE_TEMP_SENSOR, 5)// - .m(MasterChannelId.RACK_2_FAILURE_BALANCING_MODULE, 8)// - .m(MasterChannelId.RACK_2_FAILURE_TEMP_SAMPLING_LINE, 9)// - .m(MasterChannelId.RACK_2_FAILURE_INTRANET_COMMUNICATION, 10)// - .m(MasterChannelId.RACK_2_FAILURE_EEPROM, 11)// - .m(MasterChannelId.RACK_2_FAILURE_INITIALIZATION, 12)// - .build() // + m(new BitsWordElement(BASE_ADDRESS_RACK_2 + 0x185, this) // + .bit(0, ClusterChannelId.RACK_2_FAILURE_SAMPLING_WIRE)// + .bit(1, ClusterChannelId.RACK_2_FAILURE_CONNECTOR_WIRE)// + .bit(2, ClusterChannelId.RACK_2_FAILURE_LTC6803)// + .bit(3, ClusterChannelId.RACK_2_FAILURE_VOLTAGE_SAMPLING)// + .bit(4, ClusterChannelId.RACK_2_FAILURE_TEMP_SAMPLING)// + .bit(5, ClusterChannelId.RACK_2_FAILURE_TEMP_SENSOR)// + .bit(8, ClusterChannelId.RACK_2_FAILURE_BALANCING_MODULE)// + .bit(9, ClusterChannelId.RACK_2_FAILURE_TEMP_SAMPLING_LINE)// + .bit(10, ClusterChannelId.RACK_2_FAILURE_INTRANET_COMMUNICATION)// + .bit(11, ClusterChannelId.RACK_2_FAILURE_EEPROM)// + .bit(12, ClusterChannelId.RACK_2_FAILURE_INITIALIZATION)// + ) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x800, Priority.LOW, // - m(MasterChannelId.RACK_2_BATTERY_000_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_000_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x800)), // - m(MasterChannelId.RACK_2_BATTERY_001_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_001_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x801)), // - m(MasterChannelId.RACK_2_BATTERY_002_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_002_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x802)), // - m(MasterChannelId.RACK_2_BATTERY_003_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_003_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x803)), // - m(MasterChannelId.RACK_2_BATTERY_004_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_004_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x804)), // - m(MasterChannelId.RACK_2_BATTERY_005_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_005_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x805)), // - m(MasterChannelId.RACK_2_BATTERY_006_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_006_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x806)), // - m(MasterChannelId.RACK_2_BATTERY_007_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_007_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x807)), // - m(MasterChannelId.RACK_2_BATTERY_008_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_008_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x808)), // - m(MasterChannelId.RACK_2_BATTERY_009_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_009_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x809)), // - m(MasterChannelId.RACK_2_BATTERY_010_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_010_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x80A)), // - m(MasterChannelId.RACK_2_BATTERY_011_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_011_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x80B)), // - m(MasterChannelId.RACK_2_BATTERY_012_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_012_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x80C)), // - m(MasterChannelId.RACK_2_BATTERY_013_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_013_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x80D)), // - m(MasterChannelId.RACK_2_BATTERY_014_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_014_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x80E)), // - m(MasterChannelId.RACK_2_BATTERY_015_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_015_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x80F)), // - m(MasterChannelId.RACK_2_BATTERY_016_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_016_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x810)), // - m(MasterChannelId.RACK_2_BATTERY_017_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_017_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x811)), // - m(MasterChannelId.RACK_2_BATTERY_018_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_018_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x812)), // - m(MasterChannelId.RACK_2_BATTERY_019_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_019_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x813)), // - m(MasterChannelId.RACK_2_BATTERY_020_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_020_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x814)), // - m(MasterChannelId.RACK_2_BATTERY_021_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_021_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x815)), // - m(MasterChannelId.RACK_2_BATTERY_022_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_022_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x816)), // - m(MasterChannelId.RACK_2_BATTERY_023_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_023_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x817)), // - m(MasterChannelId.RACK_2_BATTERY_024_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_024_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x818)), // - m(MasterChannelId.RACK_2_BATTERY_025_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_025_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x819)), // - m(MasterChannelId.RACK_2_BATTERY_026_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_026_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x81A)), // - m(MasterChannelId.RACK_2_BATTERY_027_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_027_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x81B)), // - m(MasterChannelId.RACK_2_BATTERY_028_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_028_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x81C)), // - m(MasterChannelId.RACK_2_BATTERY_029_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_029_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x81D)), // - m(MasterChannelId.RACK_2_BATTERY_030_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_030_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x81E)), // - m(MasterChannelId.RACK_2_BATTERY_031_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_031_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x81F)), // - m(MasterChannelId.RACK_2_BATTERY_032_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_032_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x820)), // - m(MasterChannelId.RACK_2_BATTERY_033_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_033_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x821)), // - m(MasterChannelId.RACK_2_BATTERY_034_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_034_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x822)), // - m(MasterChannelId.RACK_2_BATTERY_035_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_035_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x823)), // - m(MasterChannelId.RACK_2_BATTERY_036_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_036_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x824)), // - m(MasterChannelId.RACK_2_BATTERY_037_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_037_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x825)), // - m(MasterChannelId.RACK_2_BATTERY_038_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_038_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x826)), // - m(MasterChannelId.RACK_2_BATTERY_039_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_039_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x827)), // - m(MasterChannelId.RACK_2_BATTERY_040_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_040_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x828)), // - m(MasterChannelId.RACK_2_BATTERY_041_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_041_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x829)), // - m(MasterChannelId.RACK_2_BATTERY_042_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_042_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x82A)), // - m(MasterChannelId.RACK_2_BATTERY_043_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_043_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x82B)), // - m(MasterChannelId.RACK_2_BATTERY_044_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_044_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x82C)), // - m(MasterChannelId.RACK_2_BATTERY_045_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_045_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x82D)), // - m(MasterChannelId.RACK_2_BATTERY_046_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_046_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x82E)), // - m(MasterChannelId.RACK_2_BATTERY_047_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_047_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x82F)), // - m(MasterChannelId.RACK_2_BATTERY_048_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_048_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x830)), // - m(MasterChannelId.RACK_2_BATTERY_049_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_049_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x831)), // - m(MasterChannelId.RACK_2_BATTERY_050_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_050_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x832)), // - m(MasterChannelId.RACK_2_BATTERY_051_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_051_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x833)), // - m(MasterChannelId.RACK_2_BATTERY_052_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_052_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x834)), // - m(MasterChannelId.RACK_2_BATTERY_053_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_053_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x835)), // - m(MasterChannelId.RACK_2_BATTERY_054_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_054_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x836)), // - m(MasterChannelId.RACK_2_BATTERY_055_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_055_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x837)), // - m(MasterChannelId.RACK_2_BATTERY_056_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_056_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x838)), // - m(MasterChannelId.RACK_2_BATTERY_057_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_057_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x839)), // - m(MasterChannelId.RACK_2_BATTERY_058_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_058_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x83A)), // - m(MasterChannelId.RACK_2_BATTERY_059_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_059_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x83B)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x83C, Priority.LOW, // - m(MasterChannelId.RACK_2_BATTERY_060_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_060_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x83C)), // - m(MasterChannelId.RACK_2_BATTERY_061_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_061_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x83D)), // - m(MasterChannelId.RACK_2_BATTERY_062_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_062_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x83E)), // - m(MasterChannelId.RACK_2_BATTERY_063_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_063_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x83F)), // - m(MasterChannelId.RACK_2_BATTERY_064_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_064_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x840)), // - m(MasterChannelId.RACK_2_BATTERY_065_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_065_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x841)), // - m(MasterChannelId.RACK_2_BATTERY_066_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_066_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x842)), // - m(MasterChannelId.RACK_2_BATTERY_067_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_067_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x843)), // - m(MasterChannelId.RACK_2_BATTERY_068_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_068_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x844)), // - m(MasterChannelId.RACK_2_BATTERY_069_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_069_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x845)), // - m(MasterChannelId.RACK_2_BATTERY_070_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_070_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x846)), // - m(MasterChannelId.RACK_2_BATTERY_071_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_071_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x847)), // - m(MasterChannelId.RACK_2_BATTERY_072_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_072_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x848)), // - m(MasterChannelId.RACK_2_BATTERY_073_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_073_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x849)), // - m(MasterChannelId.RACK_2_BATTERY_074_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_074_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x84A)), // - m(MasterChannelId.RACK_2_BATTERY_075_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_075_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x84B)), // - m(MasterChannelId.RACK_2_BATTERY_076_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_076_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x84C)), // - m(MasterChannelId.RACK_2_BATTERY_077_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_077_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x84D)), // - m(MasterChannelId.RACK_2_BATTERY_078_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_078_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x84E)), // - m(MasterChannelId.RACK_2_BATTERY_079_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_079_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x84F)), // - m(MasterChannelId.RACK_2_BATTERY_080_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_080_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x850)), // - m(MasterChannelId.RACK_2_BATTERY_081_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_081_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x851)), // - m(MasterChannelId.RACK_2_BATTERY_082_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_082_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x852)), // - m(MasterChannelId.RACK_2_BATTERY_083_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_083_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x853)), // - m(MasterChannelId.RACK_2_BATTERY_084_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_084_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x854)), // - m(MasterChannelId.RACK_2_BATTERY_085_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_085_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x855)), // - m(MasterChannelId.RACK_2_BATTERY_086_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_086_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x856)), // - m(MasterChannelId.RACK_2_BATTERY_087_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_087_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x857)), // - m(MasterChannelId.RACK_2_BATTERY_088_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_088_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x858)), // - m(MasterChannelId.RACK_2_BATTERY_089_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_089_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x859)), // - m(MasterChannelId.RACK_2_BATTERY_090_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_090_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x85A)), // - m(MasterChannelId.RACK_2_BATTERY_091_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_091_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x85B)), // - m(MasterChannelId.RACK_2_BATTERY_092_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_092_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x85C)), // - m(MasterChannelId.RACK_2_BATTERY_093_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_093_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x85D)), // - m(MasterChannelId.RACK_2_BATTERY_094_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_094_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x85E)), // - m(MasterChannelId.RACK_2_BATTERY_095_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_095_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x85F)), // - m(MasterChannelId.RACK_2_BATTERY_096_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_096_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x860)), // - m(MasterChannelId.RACK_2_BATTERY_097_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_097_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x861)), // - m(MasterChannelId.RACK_2_BATTERY_098_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_098_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x862)), // - m(MasterChannelId.RACK_2_BATTERY_099_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_099_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x863)), // - m(MasterChannelId.RACK_2_BATTERY_100_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_100_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x864)), // - m(MasterChannelId.RACK_2_BATTERY_101_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_101_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x865)), // - m(MasterChannelId.RACK_2_BATTERY_102_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_102_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x866)), // - m(MasterChannelId.RACK_2_BATTERY_103_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_103_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x867)), // - m(MasterChannelId.RACK_2_BATTERY_104_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_104_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x868)), // - m(MasterChannelId.RACK_2_BATTERY_105_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_105_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x869)), // - m(MasterChannelId.RACK_2_BATTERY_106_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_106_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x86A)), // - m(MasterChannelId.RACK_2_BATTERY_107_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_107_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x86B)), // - m(MasterChannelId.RACK_2_BATTERY_108_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_108_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x86C)), // - m(MasterChannelId.RACK_2_BATTERY_109_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_109_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x86D)), // - m(MasterChannelId.RACK_2_BATTERY_110_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_110_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x86E)), // - m(MasterChannelId.RACK_2_BATTERY_111_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_111_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x86F)), // - m(MasterChannelId.RACK_2_BATTERY_112_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_112_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x870)), // - m(MasterChannelId.RACK_2_BATTERY_113_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_113_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x871)), // - m(MasterChannelId.RACK_2_BATTERY_114_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_114_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x872)), // - m(MasterChannelId.RACK_2_BATTERY_115_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_115_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x873)), // - m(MasterChannelId.RACK_2_BATTERY_116_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_116_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x874)), // - m(MasterChannelId.RACK_2_BATTERY_117_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_117_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x875)), // - m(MasterChannelId.RACK_2_BATTERY_118_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_118_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x876)), // - m(MasterChannelId.RACK_2_BATTERY_119_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_119_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x877)) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x878, Priority.LOW, // - m(MasterChannelId.RACK_2_BATTERY_120_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_120_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x878)), // - m(MasterChannelId.RACK_2_BATTERY_121_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_121_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x879)), // - m(MasterChannelId.RACK_2_BATTERY_122_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_122_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x87A)), // - m(MasterChannelId.RACK_2_BATTERY_123_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_123_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x87B)), // - m(MasterChannelId.RACK_2_BATTERY_124_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_124_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x87C)), // - m(MasterChannelId.RACK_2_BATTERY_125_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_125_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x87D)), // - m(MasterChannelId.RACK_2_BATTERY_126_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_126_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x87E)), // - m(MasterChannelId.RACK_2_BATTERY_127_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_127_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x87F)), // - m(MasterChannelId.RACK_2_BATTERY_128_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_128_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x880)), // - m(MasterChannelId.RACK_2_BATTERY_129_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_129_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x881)), // - m(MasterChannelId.RACK_2_BATTERY_130_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_130_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x882)), // - m(MasterChannelId.RACK_2_BATTERY_131_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_131_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x883)), // - m(MasterChannelId.RACK_2_BATTERY_132_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_132_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x884)), // - m(MasterChannelId.RACK_2_BATTERY_133_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_133_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x885)), // - m(MasterChannelId.RACK_2_BATTERY_134_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_134_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x886)), // - m(MasterChannelId.RACK_2_BATTERY_135_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_135_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x887)), // - m(MasterChannelId.RACK_2_BATTERY_136_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_136_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x888)), // - m(MasterChannelId.RACK_2_BATTERY_137_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_137_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x889)), // - m(MasterChannelId.RACK_2_BATTERY_138_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_138_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x88A)), // - m(MasterChannelId.RACK_2_BATTERY_139_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_139_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x88B)), // - m(MasterChannelId.RACK_2_BATTERY_140_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_140_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x88C)), // - m(MasterChannelId.RACK_2_BATTERY_141_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_141_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x88D)), // - m(MasterChannelId.RACK_2_BATTERY_142_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_142_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x88E)), // - m(MasterChannelId.RACK_2_BATTERY_143_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_143_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x88F)), // - m(MasterChannelId.RACK_2_BATTERY_144_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_144_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x890)), // - m(MasterChannelId.RACK_2_BATTERY_145_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_145_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x891)), // - m(MasterChannelId.RACK_2_BATTERY_146_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_146_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x892)), // - m(MasterChannelId.RACK_2_BATTERY_147_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_147_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x893)), // - m(MasterChannelId.RACK_2_BATTERY_148_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_148_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x894)), // - m(MasterChannelId.RACK_2_BATTERY_149_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_149_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x895)), // - m(MasterChannelId.RACK_2_BATTERY_150_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_150_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x896)), // - m(MasterChannelId.RACK_2_BATTERY_151_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_151_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x897)), // - m(MasterChannelId.RACK_2_BATTERY_152_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_152_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x898)), // - m(MasterChannelId.RACK_2_BATTERY_153_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_153_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x899)), // - m(MasterChannelId.RACK_2_BATTERY_154_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_154_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x89A)), // - m(MasterChannelId.RACK_2_BATTERY_155_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_155_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x89B)), // - m(MasterChannelId.RACK_2_BATTERY_156_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_156_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x89C)), // - m(MasterChannelId.RACK_2_BATTERY_157_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_157_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x89D)), // - m(MasterChannelId.RACK_2_BATTERY_158_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_158_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x89E)), // - m(MasterChannelId.RACK_2_BATTERY_159_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_159_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x89F)), // - m(MasterChannelId.RACK_2_BATTERY_160_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_160_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A0)), // - m(MasterChannelId.RACK_2_BATTERY_161_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_161_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A1)), // - m(MasterChannelId.RACK_2_BATTERY_162_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_162_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A2)), // - m(MasterChannelId.RACK_2_BATTERY_163_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_163_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A3)), // - m(MasterChannelId.RACK_2_BATTERY_164_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_164_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A4)), // - m(MasterChannelId.RACK_2_BATTERY_165_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_165_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A5)), // - m(MasterChannelId.RACK_2_BATTERY_166_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_166_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A6)), // - m(MasterChannelId.RACK_2_BATTERY_167_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_167_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A7)), // - m(MasterChannelId.RACK_2_BATTERY_168_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_168_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A8)), // - m(MasterChannelId.RACK_2_BATTERY_169_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_169_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8A9)), // - m(MasterChannelId.RACK_2_BATTERY_170_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_170_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8AA)), // - m(MasterChannelId.RACK_2_BATTERY_171_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_171_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8AB)), // - m(MasterChannelId.RACK_2_BATTERY_172_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_172_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8AC)), // - m(MasterChannelId.RACK_2_BATTERY_173_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_173_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8AD)), // - m(MasterChannelId.RACK_2_BATTERY_174_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_174_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8AE)), // - m(MasterChannelId.RACK_2_BATTERY_175_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_175_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8AF)), // - m(MasterChannelId.RACK_2_BATTERY_176_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_176_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B0)), // - m(MasterChannelId.RACK_2_BATTERY_177_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_177_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B1)), // - m(MasterChannelId.RACK_2_BATTERY_178_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_178_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B2)), // - m(MasterChannelId.RACK_2_BATTERY_179_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_179_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B3)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0x8B4, Priority.LOW, // - m(MasterChannelId.RACK_2_BATTERY_180_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_180_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B4)), // - m(MasterChannelId.RACK_2_BATTERY_181_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_181_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B5)), // - m(MasterChannelId.RACK_2_BATTERY_182_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_182_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B6)), // - m(MasterChannelId.RACK_2_BATTERY_183_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_183_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B7)), // - m(MasterChannelId.RACK_2_BATTERY_184_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_184_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B8)), // - m(MasterChannelId.RACK_2_BATTERY_185_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_185_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8B9)), // - m(MasterChannelId.RACK_2_BATTERY_186_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_186_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8BA)), // - m(MasterChannelId.RACK_2_BATTERY_187_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_187_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8BB)), // - m(MasterChannelId.RACK_2_BATTERY_188_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_188_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8BC)), // - m(MasterChannelId.RACK_2_BATTERY_189_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_189_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8BD)), // - m(MasterChannelId.RACK_2_BATTERY_190_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_190_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8BE)), // - m(MasterChannelId.RACK_2_BATTERY_191_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_191_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8BF)), // - m(MasterChannelId.RACK_2_BATTERY_192_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_192_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C0)), // - m(MasterChannelId.RACK_2_BATTERY_193_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_193_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C1)), // - m(MasterChannelId.RACK_2_BATTERY_194_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_194_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C2)), // - m(MasterChannelId.RACK_2_BATTERY_195_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_195_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C3)), // - m(MasterChannelId.RACK_2_BATTERY_196_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_196_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C4)), // - m(MasterChannelId.RACK_2_BATTERY_197_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_197_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C5)), // - m(MasterChannelId.RACK_2_BATTERY_198_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_198_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C6)), // - m(MasterChannelId.RACK_2_BATTERY_199_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_199_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C7)), // - m(MasterChannelId.RACK_2_BATTERY_200_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_200_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C8)), // - m(MasterChannelId.RACK_2_BATTERY_201_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_201_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8C9)), // - m(MasterChannelId.RACK_2_BATTERY_202_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_202_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8CA)), // - m(MasterChannelId.RACK_2_BATTERY_203_VOLTAGE, + m(ClusterChannelId.RACK_2_BATTERY_203_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0x8CB)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0xC00, Priority.LOW, // - m(MasterChannelId.RACK_2_BATTERY_000_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_000_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC00)), // - m(MasterChannelId.RACK_2_BATTERY_001_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_001_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC01)), // - m(MasterChannelId.RACK_2_BATTERY_002_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_002_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC02)), // - m(MasterChannelId.RACK_2_BATTERY_003_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_003_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC03)), // - m(MasterChannelId.RACK_2_BATTERY_004_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_004_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC04)), // - m(MasterChannelId.RACK_2_BATTERY_005_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_005_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC05)), // - m(MasterChannelId.RACK_2_BATTERY_006_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_006_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC06)), // - m(MasterChannelId.RACK_2_BATTERY_007_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_007_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC07)), // - m(MasterChannelId.RACK_2_BATTERY_008_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_008_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC08)), // - m(MasterChannelId.RACK_2_BATTERY_009_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_009_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC09)), // - m(MasterChannelId.RACK_2_BATTERY_010_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_010_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC0A)), // - m(MasterChannelId.RACK_2_BATTERY_011_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_011_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC0B)), // - m(MasterChannelId.RACK_2_BATTERY_012_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_012_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC0C)), // - m(MasterChannelId.RACK_2_BATTERY_013_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_013_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC0D)), // - m(MasterChannelId.RACK_2_BATTERY_014_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_014_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC0E)), // - m(MasterChannelId.RACK_2_BATTERY_015_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_015_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC0F)), // - m(MasterChannelId.RACK_2_BATTERY_016_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_016_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC10)), // - m(MasterChannelId.RACK_2_BATTERY_017_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_017_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC11)), // - m(MasterChannelId.RACK_2_BATTERY_018_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_018_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC12)), // - m(MasterChannelId.RACK_2_BATTERY_019_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_019_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC13)), // - m(MasterChannelId.RACK_2_BATTERY_020_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_020_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC14)), // - m(MasterChannelId.RACK_2_BATTERY_021_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_021_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC15)), // - m(MasterChannelId.RACK_2_BATTERY_022_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_022_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC16)), // - m(MasterChannelId.RACK_2_BATTERY_023_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_023_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC17)), // - m(MasterChannelId.RACK_2_BATTERY_024_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_024_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC18)), // - m(MasterChannelId.RACK_2_BATTERY_025_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_025_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC19)), // - m(MasterChannelId.RACK_2_BATTERY_026_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_026_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC1A)), // - m(MasterChannelId.RACK_2_BATTERY_027_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_027_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC1B)), // - m(MasterChannelId.RACK_2_BATTERY_028_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_028_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC1C)), // - m(MasterChannelId.RACK_2_BATTERY_029_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_029_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC1D)), // - m(MasterChannelId.RACK_2_BATTERY_030_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_030_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC1E)), // - m(MasterChannelId.RACK_2_BATTERY_031_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_031_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC1F)), // - m(MasterChannelId.RACK_2_BATTERY_032_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_032_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC20)), // - m(MasterChannelId.RACK_2_BATTERY_033_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_033_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC21)), // - m(MasterChannelId.RACK_2_BATTERY_034_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_034_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC22)), // - m(MasterChannelId.RACK_2_BATTERY_035_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_035_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC23)), // - m(MasterChannelId.RACK_2_BATTERY_036_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_036_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC24)), // - m(MasterChannelId.RACK_2_BATTERY_037_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_037_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC25)), // - m(MasterChannelId.RACK_2_BATTERY_038_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_038_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC26)), // - m(MasterChannelId.RACK_2_BATTERY_039_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_039_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC27)), // - m(MasterChannelId.RACK_2_BATTERY_040_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_040_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC28)), // - m(MasterChannelId.RACK_2_BATTERY_041_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_041_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC29)), // - m(MasterChannelId.RACK_2_BATTERY_042_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_042_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC2A)), // - m(MasterChannelId.RACK_2_BATTERY_043_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_043_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC2B)), // - m(MasterChannelId.RACK_2_BATTERY_044_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_044_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC2C)), // - m(MasterChannelId.RACK_2_BATTERY_045_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_045_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC2D)), // - m(MasterChannelId.RACK_2_BATTERY_046_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_046_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC2E)), // - m(MasterChannelId.RACK_2_BATTERY_047_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_047_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC2F)), // - m(MasterChannelId.RACK_2_BATTERY_048_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_048_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC30)), // - m(MasterChannelId.RACK_2_BATTERY_049_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_049_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC31)), // - m(MasterChannelId.RACK_2_BATTERY_050_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_050_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC32)), // - m(MasterChannelId.RACK_2_BATTERY_051_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_051_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC33)), // - m(MasterChannelId.RACK_2_BATTERY_052_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_052_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC34)), // - m(MasterChannelId.RACK_2_BATTERY_053_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_053_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC35)), // - m(MasterChannelId.RACK_2_BATTERY_054_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_054_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC36)), // - m(MasterChannelId.RACK_2_BATTERY_055_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_055_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC37)), // - m(MasterChannelId.RACK_2_BATTERY_056_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_056_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC38)), // - m(MasterChannelId.RACK_2_BATTERY_057_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_057_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC39)), // - m(MasterChannelId.RACK_2_BATTERY_058_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_058_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC3A)), // - m(MasterChannelId.RACK_2_BATTERY_059_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_059_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC3B)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_2 + 0xC3C, Priority.LOW, // - m(MasterChannelId.RACK_2_BATTERY_060_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_060_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC3C)), // - m(MasterChannelId.RACK_2_BATTERY_061_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_061_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC3D)), // - m(MasterChannelId.RACK_2_BATTERY_062_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_062_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC3E)), // - m(MasterChannelId.RACK_2_BATTERY_063_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_063_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC3F)), // - m(MasterChannelId.RACK_2_BATTERY_064_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_064_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC40)), // - m(MasterChannelId.RACK_2_BATTERY_065_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_065_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC41)), // - m(MasterChannelId.RACK_2_BATTERY_066_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_066_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC42)), // - m(MasterChannelId.RACK_2_BATTERY_067_TEMPERATURE, + m(ClusterChannelId.RACK_2_BATTERY_067_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_2 + 0xC43)) // ) // })); @@ -1846,641 +1851,641 @@ protected ModbusProtocol defineModbusProtocol() { tasks.addAll(Arrays.asList(new Task[] { new FC16WriteRegistersTask(BASE_ADDRESS_RACK_3 + 0x1, // - m(MasterChannelId.RACK_3_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x1)) // + m(ClusterChannelId.RACK_3_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x1)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x1, Priority.HIGH, // - m(MasterChannelId.RACK_3_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x1)) // + m(ClusterChannelId.RACK_3_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x1)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x100, Priority.LOW, // - m(MasterChannelId.RACK_3_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x100), // + m(ClusterChannelId.RACK_3_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x100), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_3_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x101), // + m(ClusterChannelId.RACK_3_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x101), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_3_CHARGE_INDICATION, + m(ClusterChannelId.RACK_3_CHARGE_INDICATION, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x102)), // - m(MasterChannelId.RACK_3_SOC, + m(ClusterChannelId.RACK_3_SOC, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x103).onUpdateCallback(val -> { recalculateSoc(); })), // - m(MasterChannelId.RACK_3_SOH, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x104)), // - m(MasterChannelId.RACK_3_MAX_CELL_VOLTAGE_ID, + m(ClusterChannelId.RACK_3_SOH, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x104)), // + m(ClusterChannelId.RACK_3_MAX_CELL_VOLTAGE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x105)), // - m(MasterChannelId.RACK_3_MAX_CELL_VOLTAGE, + m(ClusterChannelId.RACK_3_MAX_CELL_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x106)), // - m(MasterChannelId.RACK_3_MIN_CELL_VOLTAGE_ID, + m(ClusterChannelId.RACK_3_MIN_CELL_VOLTAGE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x107)), // - m(MasterChannelId.RACK_3_MIN_CELL_VOLTAGE, + m(ClusterChannelId.RACK_3_MIN_CELL_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x108)), // - m(MasterChannelId.RACK_3_MAX_CELL_TEMPERATURE_ID, + m(ClusterChannelId.RACK_3_MAX_CELL_TEMPERATURE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x109)), // - m(MasterChannelId.RACK_3_MAX_CELL_TEMPERATURE, + m(ClusterChannelId.RACK_3_MAX_CELL_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x10A)), // - m(MasterChannelId.RACK_3_MIN_CELL_TEMPERATURE_ID, + m(ClusterChannelId.RACK_3_MIN_CELL_TEMPERATURE_ID, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x10B)), // - m(MasterChannelId.RACK_3_MIN_CELL_TEMPERATURE, + m(ClusterChannelId.RACK_3_MIN_CELL_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x10C)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x140, Priority.LOW, // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x140)) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH, 0) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH, 1) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_CHA_CURRENT_HIGH, 2) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_LOW, 3) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW, 4) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH, 5) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH, 6) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW, 7) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH, 14) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x141)) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH, 0) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH, 1) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CHA_CURRENT_HIGH, 2) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_VOLTAGE_LOW, 3) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW, 4) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH, 5) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH, 6) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW, 7) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_SOC_LOW, 8) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH, 9) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH, 11) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_INSULATION_LOW, 12) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH, 13) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH, 14) // - .m(MasterChannelId.RACK_3_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - m(MasterChannelId.RACK_3_RUN_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x142)) // + m(new BitsWordElement(BASE_ADDRESS_RACK_3 + 0x140, this) // + .bit(0, ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH) // + .bit(1, ClusterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH) // + .bit(2, ClusterChannelId.RACK_3_ALARM_LEVEL_2_CHA_CURRENT_HIGH) // + .bit(3, ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_VOLTAGE_LOW) // + .bit(4, ClusterChannelId.RACK_3_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW) // + .bit(5, ClusterChannelId.RACK_3_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH) // + .bit(6, ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH) // + .bit(7, ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW) // + .bit(14, ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH) // + .bit(15, ClusterChannelId.RACK_3_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW) // + ), // + m(new BitsWordElement(BASE_ADDRESS_RACK_3 + 0x141, this) // + .bit(0, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH) // + .bit(1, ClusterChannelId.RACK_3_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH) // + .bit(2, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CHA_CURRENT_HIGH) // + .bit(3, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_VOLTAGE_LOW) // + .bit(4, ClusterChannelId.RACK_3_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW) // + .bit(5, ClusterChannelId.RACK_3_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH) // + .bit(6, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH) // + .bit(7, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW) // + .bit(8, ClusterChannelId.RACK_3_ALARM_LEVEL_1_SOC_LOW) // + .bit(9, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH) // + .bit(11, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH) // + .bit(12, ClusterChannelId.RACK_3_ALARM_LEVEL_1_INSULATION_LOW) // + .bit(13, ClusterChannelId.RACK_3_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH) // + .bit(14, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH) // + .bit(15, ClusterChannelId.RACK_3_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW) // + ), // + m(ClusterChannelId.RACK_3_RUN_STATE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x142)) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x160, Priority.HIGH, // - m(MasterChannelId.RACK_3_MAX_CHARGE_CURRENT, + m(ClusterChannelId.RACK_3_MAX_CHARGE_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x160), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(MasterChannelId.RACK_3_MAX_DISCHARGE_CURRENT, + m(ClusterChannelId.RACK_3_MAX_DISCHARGE_CURRENT, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x161), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x185, Priority.LOW, // - bm(new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x185)) // - .m(MasterChannelId.RACK_3_FAILURE_SAMPLING_WIRE, 0)// - .m(MasterChannelId.RACK_3_FAILURE_CONNECTOR_WIRE, 1)// - .m(MasterChannelId.RACK_3_FAILURE_LTC6803, 2)// - .m(MasterChannelId.RACK_3_FAILURE_VOLTAGE_SAMPLING, 3)// - .m(MasterChannelId.RACK_3_FAILURE_TEMP_SAMPLING, 4)// - .m(MasterChannelId.RACK_3_FAILURE_TEMP_SENSOR, 5)// - .m(MasterChannelId.RACK_3_FAILURE_BALANCING_MODULE, 8)// - .m(MasterChannelId.RACK_3_FAILURE_TEMP_SAMPLING_LINE, 9)// - .m(MasterChannelId.RACK_3_FAILURE_INTRANET_COMMUNICATION, 10)// - .m(MasterChannelId.RACK_3_FAILURE_EEPROM, 11)// - .m(MasterChannelId.RACK_3_FAILURE_INITIALIZATION, 12)// - .build() // + m(new BitsWordElement(BASE_ADDRESS_RACK_3 + 0x185, this) // + .bit(0, ClusterChannelId.RACK_3_FAILURE_SAMPLING_WIRE)// + .bit(1, ClusterChannelId.RACK_3_FAILURE_CONNECTOR_WIRE)// + .bit(2, ClusterChannelId.RACK_3_FAILURE_LTC6803)// + .bit(3, ClusterChannelId.RACK_3_FAILURE_VOLTAGE_SAMPLING)// + .bit(4, ClusterChannelId.RACK_3_FAILURE_TEMP_SAMPLING)// + .bit(5, ClusterChannelId.RACK_3_FAILURE_TEMP_SENSOR)// + .bit(8, ClusterChannelId.RACK_3_FAILURE_BALANCING_MODULE)// + .bit(9, ClusterChannelId.RACK_3_FAILURE_TEMP_SAMPLING_LINE)// + .bit(10, ClusterChannelId.RACK_3_FAILURE_INTRANET_COMMUNICATION)// + .bit(11, ClusterChannelId.RACK_3_FAILURE_EEPROM)// + .bit(12, ClusterChannelId.RACK_3_FAILURE_INITIALIZATION)// + ) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x800, Priority.LOW, // - m(MasterChannelId.RACK_3_BATTERY_000_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_000_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x800)), // - m(MasterChannelId.RACK_3_BATTERY_001_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_001_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x801)), // - m(MasterChannelId.RACK_3_BATTERY_002_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_002_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x802)), // - m(MasterChannelId.RACK_3_BATTERY_003_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_003_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x803)), // - m(MasterChannelId.RACK_3_BATTERY_004_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_004_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x804)), // - m(MasterChannelId.RACK_3_BATTERY_005_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_005_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x805)), // - m(MasterChannelId.RACK_3_BATTERY_006_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_006_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x806)), // - m(MasterChannelId.RACK_3_BATTERY_007_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_007_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x807)), // - m(MasterChannelId.RACK_3_BATTERY_008_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_008_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x808)), // - m(MasterChannelId.RACK_3_BATTERY_009_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_009_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x809)), // - m(MasterChannelId.RACK_3_BATTERY_010_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_010_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x80A)), // - m(MasterChannelId.RACK_3_BATTERY_011_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_011_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x80B)), // - m(MasterChannelId.RACK_3_BATTERY_012_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_012_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x80C)), // - m(MasterChannelId.RACK_3_BATTERY_013_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_013_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x80D)), // - m(MasterChannelId.RACK_3_BATTERY_014_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_014_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x80E)), // - m(MasterChannelId.RACK_3_BATTERY_015_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_015_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x80F)), // - m(MasterChannelId.RACK_3_BATTERY_016_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_016_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x810)), // - m(MasterChannelId.RACK_3_BATTERY_017_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_017_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x811)), // - m(MasterChannelId.RACK_3_BATTERY_018_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_018_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x812)), // - m(MasterChannelId.RACK_3_BATTERY_019_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_019_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x813)), // - m(MasterChannelId.RACK_3_BATTERY_020_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_020_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x814)), // - m(MasterChannelId.RACK_3_BATTERY_021_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_021_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x815)), // - m(MasterChannelId.RACK_3_BATTERY_022_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_022_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x816)), // - m(MasterChannelId.RACK_3_BATTERY_023_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_023_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x817)), // - m(MasterChannelId.RACK_3_BATTERY_024_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_024_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x818)), // - m(MasterChannelId.RACK_3_BATTERY_025_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_025_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x819)), // - m(MasterChannelId.RACK_3_BATTERY_026_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_026_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x81A)), // - m(MasterChannelId.RACK_3_BATTERY_027_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_027_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x81B)), // - m(MasterChannelId.RACK_3_BATTERY_028_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_028_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x81C)), // - m(MasterChannelId.RACK_3_BATTERY_029_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_029_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x81D)), // - m(MasterChannelId.RACK_3_BATTERY_030_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_030_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x81E)), // - m(MasterChannelId.RACK_3_BATTERY_031_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_031_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x81F)), // - m(MasterChannelId.RACK_3_BATTERY_032_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_032_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x820)), // - m(MasterChannelId.RACK_3_BATTERY_033_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_033_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x821)), // - m(MasterChannelId.RACK_3_BATTERY_034_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_034_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x822)), // - m(MasterChannelId.RACK_3_BATTERY_035_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_035_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x823)), // - m(MasterChannelId.RACK_3_BATTERY_036_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_036_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x824)), // - m(MasterChannelId.RACK_3_BATTERY_037_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_037_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x825)), // - m(MasterChannelId.RACK_3_BATTERY_038_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_038_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x826)), // - m(MasterChannelId.RACK_3_BATTERY_039_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_039_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x827)), // - m(MasterChannelId.RACK_3_BATTERY_040_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_040_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x828)), // - m(MasterChannelId.RACK_3_BATTERY_041_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_041_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x829)), // - m(MasterChannelId.RACK_3_BATTERY_042_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_042_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x82A)), // - m(MasterChannelId.RACK_3_BATTERY_043_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_043_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x82B)), // - m(MasterChannelId.RACK_3_BATTERY_044_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_044_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x82C)), // - m(MasterChannelId.RACK_3_BATTERY_045_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_045_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x82D)), // - m(MasterChannelId.RACK_3_BATTERY_046_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_046_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x82E)), // - m(MasterChannelId.RACK_3_BATTERY_047_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_047_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x82F)), // - m(MasterChannelId.RACK_3_BATTERY_048_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_048_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x830)), // - m(MasterChannelId.RACK_3_BATTERY_049_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_049_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x831)), // - m(MasterChannelId.RACK_3_BATTERY_050_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_050_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x832)), // - m(MasterChannelId.RACK_3_BATTERY_051_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_051_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x833)), // - m(MasterChannelId.RACK_3_BATTERY_052_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_052_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x834)), // - m(MasterChannelId.RACK_3_BATTERY_053_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_053_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x835)), // - m(MasterChannelId.RACK_3_BATTERY_054_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_054_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x836)), // - m(MasterChannelId.RACK_3_BATTERY_055_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_055_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x837)), // - m(MasterChannelId.RACK_3_BATTERY_056_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_056_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x838)), // - m(MasterChannelId.RACK_3_BATTERY_057_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_057_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x839)), // - m(MasterChannelId.RACK_3_BATTERY_058_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_058_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x83A)), // - m(MasterChannelId.RACK_3_BATTERY_059_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_059_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x83B)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x83C, Priority.LOW, // - m(MasterChannelId.RACK_3_BATTERY_060_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_060_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x83C)), // - m(MasterChannelId.RACK_3_BATTERY_061_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_061_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x83D)), // - m(MasterChannelId.RACK_3_BATTERY_062_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_062_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x83E)), // - m(MasterChannelId.RACK_3_BATTERY_063_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_063_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x83F)), // - m(MasterChannelId.RACK_3_BATTERY_064_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_064_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x840)), // - m(MasterChannelId.RACK_3_BATTERY_065_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_065_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x841)), // - m(MasterChannelId.RACK_3_BATTERY_066_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_066_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x842)), // - m(MasterChannelId.RACK_3_BATTERY_067_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_067_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x843)), // - m(MasterChannelId.RACK_3_BATTERY_068_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_068_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x844)), // - m(MasterChannelId.RACK_3_BATTERY_069_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_069_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x845)), // - m(MasterChannelId.RACK_3_BATTERY_070_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_070_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x846)), // - m(MasterChannelId.RACK_3_BATTERY_071_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_071_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x847)), // - m(MasterChannelId.RACK_3_BATTERY_072_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_072_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x848)), // - m(MasterChannelId.RACK_3_BATTERY_073_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_073_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x849)), // - m(MasterChannelId.RACK_3_BATTERY_074_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_074_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x84A)), // - m(MasterChannelId.RACK_3_BATTERY_075_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_075_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x84B)), // - m(MasterChannelId.RACK_3_BATTERY_076_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_076_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x84C)), // - m(MasterChannelId.RACK_3_BATTERY_077_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_077_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x84D)), // - m(MasterChannelId.RACK_3_BATTERY_078_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_078_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x84E)), // - m(MasterChannelId.RACK_3_BATTERY_079_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_079_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x84F)), // - m(MasterChannelId.RACK_3_BATTERY_080_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_080_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x850)), // - m(MasterChannelId.RACK_3_BATTERY_081_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_081_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x851)), // - m(MasterChannelId.RACK_3_BATTERY_082_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_082_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x852)), // - m(MasterChannelId.RACK_3_BATTERY_083_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_083_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x853)), // - m(MasterChannelId.RACK_3_BATTERY_084_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_084_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x854)), // - m(MasterChannelId.RACK_3_BATTERY_085_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_085_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x855)), // - m(MasterChannelId.RACK_3_BATTERY_086_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_086_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x856)), // - m(MasterChannelId.RACK_3_BATTERY_087_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_087_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x857)), // - m(MasterChannelId.RACK_3_BATTERY_088_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_088_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x858)), // - m(MasterChannelId.RACK_3_BATTERY_089_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_089_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x859)), // - m(MasterChannelId.RACK_3_BATTERY_090_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_090_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x85A)), // - m(MasterChannelId.RACK_3_BATTERY_091_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_091_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x85B)), // - m(MasterChannelId.RACK_3_BATTERY_092_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_092_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x85C)), // - m(MasterChannelId.RACK_3_BATTERY_093_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_093_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x85D)), // - m(MasterChannelId.RACK_3_BATTERY_094_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_094_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x85E)), // - m(MasterChannelId.RACK_3_BATTERY_095_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_095_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x85F)), // - m(MasterChannelId.RACK_3_BATTERY_096_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_096_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x860)), // - m(MasterChannelId.RACK_3_BATTERY_097_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_097_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x861)), // - m(MasterChannelId.RACK_3_BATTERY_098_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_098_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x862)), // - m(MasterChannelId.RACK_3_BATTERY_099_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_099_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x863)), // - m(MasterChannelId.RACK_3_BATTERY_100_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_100_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x864)), // - m(MasterChannelId.RACK_3_BATTERY_101_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_101_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x865)), // - m(MasterChannelId.RACK_3_BATTERY_102_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_102_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x866)), // - m(MasterChannelId.RACK_3_BATTERY_103_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_103_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x867)), // - m(MasterChannelId.RACK_3_BATTERY_104_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_104_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x868)), // - m(MasterChannelId.RACK_3_BATTERY_105_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_105_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x869)), // - m(MasterChannelId.RACK_3_BATTERY_106_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_106_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x86A)), // - m(MasterChannelId.RACK_3_BATTERY_107_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_107_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x86B)), // - m(MasterChannelId.RACK_3_BATTERY_108_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_108_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x86C)), // - m(MasterChannelId.RACK_3_BATTERY_109_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_109_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x86D)), // - m(MasterChannelId.RACK_3_BATTERY_110_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_110_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x86E)), // - m(MasterChannelId.RACK_3_BATTERY_111_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_111_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x86F)), // - m(MasterChannelId.RACK_3_BATTERY_112_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_112_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x870)), // - m(MasterChannelId.RACK_3_BATTERY_113_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_113_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x871)), // - m(MasterChannelId.RACK_3_BATTERY_114_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_114_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x872)), // - m(MasterChannelId.RACK_3_BATTERY_115_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_115_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x873)), // - m(MasterChannelId.RACK_3_BATTERY_116_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_116_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x874)), // - m(MasterChannelId.RACK_3_BATTERY_117_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_117_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x875)), // - m(MasterChannelId.RACK_3_BATTERY_118_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_118_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x876)), // - m(MasterChannelId.RACK_3_BATTERY_119_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_119_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x877)) // ), // new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x878, Priority.LOW, // - m(MasterChannelId.RACK_3_BATTERY_120_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_120_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x878)), // - m(MasterChannelId.RACK_3_BATTERY_121_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_121_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x879)), // - m(MasterChannelId.RACK_3_BATTERY_122_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_122_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x87A)), // - m(MasterChannelId.RACK_3_BATTERY_123_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_123_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x87B)), // - m(MasterChannelId.RACK_3_BATTERY_124_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_124_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x87C)), // - m(MasterChannelId.RACK_3_BATTERY_125_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_125_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x87D)), // - m(MasterChannelId.RACK_3_BATTERY_126_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_126_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x87E)), // - m(MasterChannelId.RACK_3_BATTERY_127_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_127_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x87F)), // - m(MasterChannelId.RACK_3_BATTERY_128_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_128_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x880)), // - m(MasterChannelId.RACK_3_BATTERY_129_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_129_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x881)), // - m(MasterChannelId.RACK_3_BATTERY_130_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_130_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x882)), // - m(MasterChannelId.RACK_3_BATTERY_131_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_131_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x883)), // - m(MasterChannelId.RACK_3_BATTERY_132_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_132_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x884)), // - m(MasterChannelId.RACK_3_BATTERY_133_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_133_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x885)), // - m(MasterChannelId.RACK_3_BATTERY_134_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_134_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x886)), // - m(MasterChannelId.RACK_3_BATTERY_135_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_135_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x887)), // - m(MasterChannelId.RACK_3_BATTERY_136_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_136_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x888)), // - m(MasterChannelId.RACK_3_BATTERY_137_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_137_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x889)), // - m(MasterChannelId.RACK_3_BATTERY_138_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_138_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x88A)), // - m(MasterChannelId.RACK_3_BATTERY_139_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_139_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x88B)), // - m(MasterChannelId.RACK_3_BATTERY_140_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_140_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x88C)), // - m(MasterChannelId.RACK_3_BATTERY_141_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_141_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x88D)), // - m(MasterChannelId.RACK_3_BATTERY_142_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_142_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x88E)), // - m(MasterChannelId.RACK_3_BATTERY_143_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_143_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x88F)), // - m(MasterChannelId.RACK_3_BATTERY_144_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_144_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x890)), // - m(MasterChannelId.RACK_3_BATTERY_145_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_145_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x891)), // - m(MasterChannelId.RACK_3_BATTERY_146_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_146_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x892)), // - m(MasterChannelId.RACK_3_BATTERY_147_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_147_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x893)), // - m(MasterChannelId.RACK_3_BATTERY_148_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_148_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x894)), // - m(MasterChannelId.RACK_3_BATTERY_149_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_149_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x895)), // - m(MasterChannelId.RACK_3_BATTERY_150_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_150_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x896)), // - m(MasterChannelId.RACK_3_BATTERY_151_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_151_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x897)), // - m(MasterChannelId.RACK_3_BATTERY_152_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_152_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x898)), // - m(MasterChannelId.RACK_3_BATTERY_153_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_153_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x899)), // - m(MasterChannelId.RACK_3_BATTERY_154_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_154_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x89A)), // - m(MasterChannelId.RACK_3_BATTERY_155_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_155_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x89B)), // - m(MasterChannelId.RACK_3_BATTERY_156_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_156_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x89C)), // - m(MasterChannelId.RACK_3_BATTERY_157_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_157_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x89D)), // - m(MasterChannelId.RACK_3_BATTERY_158_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_158_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x89E)), // - m(MasterChannelId.RACK_3_BATTERY_159_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_159_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x89F)), // - m(MasterChannelId.RACK_3_BATTERY_160_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_160_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A0)), // - m(MasterChannelId.RACK_3_BATTERY_161_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_161_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A1)), // - m(MasterChannelId.RACK_3_BATTERY_162_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_162_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A2)), // - m(MasterChannelId.RACK_3_BATTERY_163_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_163_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A3)), // - m(MasterChannelId.RACK_3_BATTERY_164_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_164_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A4)), // - m(MasterChannelId.RACK_3_BATTERY_165_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_165_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A5)), // - m(MasterChannelId.RACK_3_BATTERY_166_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_166_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A6)), // - m(MasterChannelId.RACK_3_BATTERY_167_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_167_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A7)), // - m(MasterChannelId.RACK_3_BATTERY_168_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_168_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A8)), // - m(MasterChannelId.RACK_3_BATTERY_169_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_169_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8A9)), // - m(MasterChannelId.RACK_3_BATTERY_170_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_170_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8AA)), // - m(MasterChannelId.RACK_3_BATTERY_171_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_171_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8AB)), // - m(MasterChannelId.RACK_3_BATTERY_172_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_172_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8AC)), // - m(MasterChannelId.RACK_3_BATTERY_173_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_173_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8AD)), // - m(MasterChannelId.RACK_3_BATTERY_174_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_174_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8AE)), // - m(MasterChannelId.RACK_3_BATTERY_175_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_175_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8AF)), // - m(MasterChannelId.RACK_3_BATTERY_176_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_176_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B0)), // - m(MasterChannelId.RACK_3_BATTERY_177_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_177_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B1)), // - m(MasterChannelId.RACK_3_BATTERY_178_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_178_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B2)), // - m(MasterChannelId.RACK_3_BATTERY_179_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_179_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B3)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0x8B4, Priority.LOW, // - m(MasterChannelId.RACK_3_BATTERY_180_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_180_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B4)), // - m(MasterChannelId.RACK_3_BATTERY_181_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_181_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B5)), // - m(MasterChannelId.RACK_3_BATTERY_182_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_182_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B6)), // - m(MasterChannelId.RACK_3_BATTERY_183_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_183_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B7)), // - m(MasterChannelId.RACK_3_BATTERY_184_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_184_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B8)), // - m(MasterChannelId.RACK_3_BATTERY_185_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_185_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8B9)), // - m(MasterChannelId.RACK_3_BATTERY_186_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_186_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8BA)), // - m(MasterChannelId.RACK_3_BATTERY_187_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_187_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8BB)), // - m(MasterChannelId.RACK_3_BATTERY_188_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_188_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8BC)), // - m(MasterChannelId.RACK_3_BATTERY_189_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_189_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8BD)), // - m(MasterChannelId.RACK_3_BATTERY_190_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_190_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8BE)), // - m(MasterChannelId.RACK_3_BATTERY_191_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_191_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8BF)), // - m(MasterChannelId.RACK_3_BATTERY_192_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_192_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C0)), // - m(MasterChannelId.RACK_3_BATTERY_193_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_193_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C1)), // - m(MasterChannelId.RACK_3_BATTERY_194_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_194_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C2)), // - m(MasterChannelId.RACK_3_BATTERY_195_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_195_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C3)), // - m(MasterChannelId.RACK_3_BATTERY_196_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_196_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C4)), // - m(MasterChannelId.RACK_3_BATTERY_197_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_197_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C5)), // - m(MasterChannelId.RACK_3_BATTERY_198_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_198_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C6)), // - m(MasterChannelId.RACK_3_BATTERY_199_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_199_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C7)), // - m(MasterChannelId.RACK_3_BATTERY_200_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_200_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C8)), // - m(MasterChannelId.RACK_3_BATTERY_201_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_201_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8C9)), // - m(MasterChannelId.RACK_3_BATTERY_202_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_202_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8CA)), // - m(MasterChannelId.RACK_3_BATTERY_203_VOLTAGE, + m(ClusterChannelId.RACK_3_BATTERY_203_VOLTAGE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0x8CB)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0xC00, Priority.LOW, // - m(MasterChannelId.RACK_3_BATTERY_000_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_000_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC00)), // - m(MasterChannelId.RACK_3_BATTERY_001_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_001_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC01)), // - m(MasterChannelId.RACK_3_BATTERY_002_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_002_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC02)), // - m(MasterChannelId.RACK_3_BATTERY_003_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_003_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC03)), // - m(MasterChannelId.RACK_3_BATTERY_004_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_004_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC04)), // - m(MasterChannelId.RACK_3_BATTERY_005_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_005_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC05)), // - m(MasterChannelId.RACK_3_BATTERY_006_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_006_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC06)), // - m(MasterChannelId.RACK_3_BATTERY_007_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_007_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC07)), // - m(MasterChannelId.RACK_3_BATTERY_008_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_008_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC08)), // - m(MasterChannelId.RACK_3_BATTERY_009_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_009_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC09)), // - m(MasterChannelId.RACK_3_BATTERY_010_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_010_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC0A)), // - m(MasterChannelId.RACK_3_BATTERY_011_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_011_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC0B)), // - m(MasterChannelId.RACK_3_BATTERY_012_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_012_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC0C)), // - m(MasterChannelId.RACK_3_BATTERY_013_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_013_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC0D)), // - m(MasterChannelId.RACK_3_BATTERY_014_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_014_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC0E)), // - m(MasterChannelId.RACK_3_BATTERY_015_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_015_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC0F)), // - m(MasterChannelId.RACK_3_BATTERY_016_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_016_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC10)), // - m(MasterChannelId.RACK_3_BATTERY_017_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_017_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC11)), // - m(MasterChannelId.RACK_3_BATTERY_018_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_018_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC12)), // - m(MasterChannelId.RACK_3_BATTERY_019_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_019_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC13)), // - m(MasterChannelId.RACK_3_BATTERY_020_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_020_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC14)), // - m(MasterChannelId.RACK_3_BATTERY_021_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_021_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC15)), // - m(MasterChannelId.RACK_3_BATTERY_022_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_022_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC16)), // - m(MasterChannelId.RACK_3_BATTERY_023_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_023_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC17)), // - m(MasterChannelId.RACK_3_BATTERY_024_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_024_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC18)), // - m(MasterChannelId.RACK_3_BATTERY_025_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_025_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC19)), // - m(MasterChannelId.RACK_3_BATTERY_026_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_026_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC1A)), // - m(MasterChannelId.RACK_3_BATTERY_027_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_027_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC1B)), // - m(MasterChannelId.RACK_3_BATTERY_028_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_028_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC1C)), // - m(MasterChannelId.RACK_3_BATTERY_029_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_029_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC1D)), // - m(MasterChannelId.RACK_3_BATTERY_030_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_030_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC1E)), // - m(MasterChannelId.RACK_3_BATTERY_031_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_031_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC1F)), // - m(MasterChannelId.RACK_3_BATTERY_032_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_032_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC20)), // - m(MasterChannelId.RACK_3_BATTERY_033_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_033_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC21)), // - m(MasterChannelId.RACK_3_BATTERY_034_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_034_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC22)), // - m(MasterChannelId.RACK_3_BATTERY_035_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_035_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC23)), // - m(MasterChannelId.RACK_3_BATTERY_036_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_036_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC24)), // - m(MasterChannelId.RACK_3_BATTERY_037_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_037_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC25)), // - m(MasterChannelId.RACK_3_BATTERY_038_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_038_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC26)), // - m(MasterChannelId.RACK_3_BATTERY_039_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_039_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC27)), // - m(MasterChannelId.RACK_3_BATTERY_040_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_040_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC28)), // - m(MasterChannelId.RACK_3_BATTERY_041_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_041_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC29)), // - m(MasterChannelId.RACK_3_BATTERY_042_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_042_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC2A)), // - m(MasterChannelId.RACK_3_BATTERY_043_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_043_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC2B)), // - m(MasterChannelId.RACK_3_BATTERY_044_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_044_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC2C)), // - m(MasterChannelId.RACK_3_BATTERY_045_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_045_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC2D)), // - m(MasterChannelId.RACK_3_BATTERY_046_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_046_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC2E)), // - m(MasterChannelId.RACK_3_BATTERY_047_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_047_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC2F)), // - m(MasterChannelId.RACK_3_BATTERY_048_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_048_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC30)), // - m(MasterChannelId.RACK_3_BATTERY_049_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_049_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC31)), // - m(MasterChannelId.RACK_3_BATTERY_050_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_050_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC32)), // - m(MasterChannelId.RACK_3_BATTERY_051_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_051_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC33)), // - m(MasterChannelId.RACK_3_BATTERY_052_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_052_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC34)), // - m(MasterChannelId.RACK_3_BATTERY_053_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_053_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC35)), // - m(MasterChannelId.RACK_3_BATTERY_054_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_054_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC36)), // - m(MasterChannelId.RACK_3_BATTERY_055_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_055_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC37)), // - m(MasterChannelId.RACK_3_BATTERY_056_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_056_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC38)), // - m(MasterChannelId.RACK_3_BATTERY_057_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_057_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC39)), // - m(MasterChannelId.RACK_3_BATTERY_058_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_058_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC3A)), // - m(MasterChannelId.RACK_3_BATTERY_059_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_059_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC3B)) // ), new FC3ReadRegistersTask(BASE_ADDRESS_RACK_3 + 0xC3C, Priority.LOW, // - m(MasterChannelId.RACK_3_BATTERY_060_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_060_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC3C)), // - m(MasterChannelId.RACK_3_BATTERY_061_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_061_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC3D)), // - m(MasterChannelId.RACK_3_BATTERY_062_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_062_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC3E)), // - m(MasterChannelId.RACK_3_BATTERY_063_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_063_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC3F)), // - m(MasterChannelId.RACK_3_BATTERY_064_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_064_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC40)), // - m(MasterChannelId.RACK_3_BATTERY_065_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_065_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC41)), // - m(MasterChannelId.RACK_3_BATTERY_066_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_066_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC42)), // - m(MasterChannelId.RACK_3_BATTERY_067_TEMPERATURE, + m(ClusterChannelId.RACK_3_BATTERY_067_TEMPERATURE, new UnsignedWordElement(BASE_ADDRESS_RACK_3 + 0xC43)) // ) // })); @@ -2494,7 +2499,7 @@ private void recalculateSoc() { int soc = 0; if (config.rack1IsUsed()) { - IntegerReadChannel r = this.channel(MasterChannelId.RACK_1_SOC); + IntegerReadChannel r = this.channel(ClusterChannelId.RACK_1_SOC); Optional s = r.value().asOptional(); if (s.isPresent()) { i++; @@ -2503,7 +2508,7 @@ private void recalculateSoc() { } if (config.rack2IsUsed()) { - IntegerReadChannel r = this.channel(MasterChannelId.RACK_2_SOC); + IntegerReadChannel r = this.channel(ClusterChannelId.RACK_2_SOC); Optional s = r.value().asOptional(); if (s.isPresent()) { i++; @@ -2512,7 +2517,7 @@ private void recalculateSoc() { } if (config.rack3IsUsed()) { - IntegerReadChannel r = this.channel(MasterChannelId.RACK_3_SOC); + IntegerReadChannel r = this.channel(ClusterChannelId.RACK_3_SOC); Optional s = r.value().asOptional(); if (s.isPresent()) { i++; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/MasterChannelId.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/ClusterChannelId.java similarity index 99% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/MasterChannelId.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/ClusterChannelId.java index afe8e311689..8d961f82725 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/MasterChannelId.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/ClusterChannelId.java @@ -1,12 +1,13 @@ -package io.openems.edge.battery.soltaro.master; +package io.openems.edge.battery.soltaro.cluster.versiona; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; +import io.openems.edge.battery.soltaro.State; -public enum MasterChannelId implements io.openems.edge.common.channel.ChannelId { +public enum ClusterChannelId implements io.openems.edge.common.channel.ChannelId { // EnumReadChannels STATE_MACHINE(Doc.of(State.values()) // .text("Current State of State-Machine")), // @@ -18,14 +19,10 @@ public enum MasterChannelId implements io.openems.edge.common.channel.ChannelId .accessMode(AccessMode.READ_WRITE)), // // IntegerReadChannels - CHARGE_MAX_CURRENT(Doc.of(OpenemsType.INTEGER) // + CHARGE_MAX_CURRENT_CLUSTER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE)), - DISCHARGE_MAX_CURRENT(Doc.of(OpenemsType.INTEGER) // + DISCHARGE_MAX_CURRENT_CLUSTER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE)), - CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // - VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT)), // // StateChannels MASTER_ALARM_PCS_OUT_OF_CONTROL(Doc.of(Level.INFO) // @@ -2350,7 +2347,7 @@ public enum MasterChannelId implements io.openems.edge.common.channel.ChannelId private final Doc doc; - private MasterChannelId(Doc doc) { + private ClusterChannelId(Doc doc) { this.doc = doc; } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ClusterRunState.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/ClusterRunState.java similarity index 84% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ClusterRunState.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/ClusterRunState.java index b53d6734a7e..3663a2ee078 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ClusterRunState.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/ClusterRunState.java @@ -1,6 +1,6 @@ -package io.openems.edge.battery.soltaro; +package io.openems.edge.battery.soltaro.cluster.versiona; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ClusterRunState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Config.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Config.java similarity index 78% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Config.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Config.java index 0f6d755488f..313c7074d7c 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Config.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Config.java @@ -1,4 +1,4 @@ -package io.openems.edge.battery.soltaro.master; +package io.openems.edge.battery.soltaro.cluster.versiona; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @@ -6,47 +6,51 @@ import io.openems.edge.battery.soltaro.BatteryState; @ObjectClassDefinition( // - name = "BMS FENECON Soltaro Master", // + name = "BMS Soltaro Cluster Version A", // description = "Implements the Soltaro master battery rack system.") @interface Config { - String service_pid(); + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "bms0"; + @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 = "Modbus-ID", description = "ID of Modbus brige; ! Soltaro Master needs baudrate of 57600 !") - String modbus_id(); - + @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus brige; ! Soltaro Cluster needs baudrate of 57600 !") + String modbus_id() default "modbus0"; + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") int modbusUnitId() default 1; - + @AttributeDefinition(name = "Battery state", description = "Switches the battery into the given state, if default is used, battery state is set automatically") BatteryState batteryState() default BatteryState.DEFAULT; - + @AttributeDefinition(name = "Rack 1 Usage", description = "Determines whether rack 1 is used") boolean rack1IsUsed() default true; - + @AttributeDefinition(name = "Rack 2 Usage", description = "Determines whether rack 2 is used") boolean rack2IsUsed() default true; - + @AttributeDefinition(name = "Rack 3 Usage", description = "Determines whether rack 3 is used") boolean rack3IsUsed() default true; - + @AttributeDefinition(name = "Error Level 2 Delay", description = "Sets the delay time in seconds how long the system should be stopped after an error level 2 has occurred") int errorLevel2Delay() default 600; - + @AttributeDefinition(name = "Max Start Attempts", description = "Sets the counter how many time the system should try to start") int maxStartAppempts() default 5; - + @AttributeDefinition(name = "Max Start Time", description = "Max Time in seconds allowed for starting the system") int maxStartTime() default 20; - + @AttributeDefinition(name = "Start Not Successful Delay Time", description = "Sets the delay time in seconds how long the system should be stopped if it was not able to start") int startUnsuccessfulDelay() default 3600; @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "BMS FENECON Soltaro Master [{id}]"; + String webconsole_configurationFactory_nameHint() default "BMS Soltaro Cluster Version A [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Enums.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Enums.java similarity index 95% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Enums.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Enums.java index 3cffc6ba3f0..0f5d2185c8e 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/Enums.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/Enums.java @@ -1,6 +1,6 @@ -package io.openems.edge.battery.soltaro.master; +package io.openems.edge.battery.soltaro.cluster.versiona; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public class Enums { diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/package-info.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/package-info.java new file mode 100644 index 00000000000..dbc5e256066 --- /dev/null +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versiona/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.edge.battery.soltaro.cluster.versiona; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Cluster.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Cluster.java new file mode 100644 index 00000000000..18600735c2e --- /dev/null +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Cluster.java @@ -0,0 +1,616 @@ +package io.openems.edge.battery.soltaro.cluster.versionb; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +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.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.event.Event; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.metatype.annotations.Designate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.battery.api.Battery; +import io.openems.edge.battery.soltaro.BatteryState; +import io.openems.edge.battery.soltaro.ModuleParameters; +import io.openems.edge.battery.soltaro.State; +import io.openems.edge.battery.soltaro.cluster.versionb.ClusterChannelId; +import io.openems.edge.battery.soltaro.cluster.versionb.Enums.ContactorControl; +import io.openems.edge.battery.soltaro.cluster.versionb.Enums.RackUsage; +import io.openems.edge.battery.soltaro.cluster.versionb.Enums.StartStop; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; +import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; +import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; +import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; +import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; +import io.openems.edge.bridge.modbus.api.task.Task; +import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.channel.EnumReadChannel; +import io.openems.edge.common.channel.EnumWriteChannel; +import io.openems.edge.common.channel.StateChannel; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.common.taskmanager.Priority; + +@Designate(ocd = Config.class, factory = true) +@Component( // + name = "Bms.Soltaro.Cluster.VersionB", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE, // + property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // +) +public class Cluster extends AbstractOpenemsModbusComponent implements Battery, OpenemsComponent, EventHandler { + + public static final int DISCHARGE_MAX_A = 0; // default value 0 to avoid damages + public static final int CHARGE_MAX_A = 0; // default value 0 to avoid damages + + private static final int ADDRESS_OFFSET_RACK_1 = 0x2000; + private static final int ADDRESS_OFFSET_RACK_2 = 0x3000; + private static final int ADDRESS_OFFSET_RACK_3 = 0x4000; + private static final int ADDRESS_OFFSET_RACK_4 = 0x5000; + private static final int ADDRESS_OFFSET_RACK_5 = 0x6000; + private static final int OFFSET_CONTACTOR_CONTROL = 0x10; + + // Helper that holds general information about single racks, independent if they + // are used or not + private static final Map RACK_INFO = createRackInfo(); + private final Logger log = LoggerFactory.getLogger(Cluster.class); + + @Reference + protected ConfigurationAdmin cm; + + // If an error has occurred, this indicates the time when next action could be + // done + private LocalDateTime errorDelayIsOver = null; + private int unsuccessfulStarts = 0; + private LocalDateTime startAttemptTime = null; + private String modbusBridgeId; + private BatteryState batteryState; + private State state = State.UNDEFINED; + private Config config; + private Map racks = new HashMap<>(); + + public Cluster() { + super(// + OpenemsComponent.ChannelId.values(), // + Battery.ChannelId.values(), // + ClusterChannelId.values() // + ); + } + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + @Activate + void activate(ComponentContext context, Config config) { + // Create racks dynamically, do this before super() call because super() uses getModbusProtocol, and it is using racks... + for (int i : config.racks()) { + this.racks.put(i, new SingleRack(i, config.numberOfSlaves(), RACK_INFO.get(i).addressOffset, this)); + } + + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + config.modbus_id()); + + this.config = config; + this.modbusBridgeId = config.modbus_id(); + this.batteryState = config.batteryState(); + + + this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(Cluster.CHARGE_MAX_A); + this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(Cluster.DISCHARGE_MAX_A); + this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE) + .setNextValue(this.config.numberOfSlaves() * ModuleParameters.MAX_VOLTAGE_MILLIVOLT.getValue() / 1000); + this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE) + .setNextValue(this.config.numberOfSlaves() * ModuleParameters.MIN_VOLTAGE_MILLIVOLT.getValue() / 1000); + this.channel(Battery.ChannelId.CAPACITY).setNextValue(this.config.racks().length * this.config.numberOfSlaves() + * ModuleParameters.CAPACITY_WH.getValue() / 1000); + } + + @Override + public void handleEvent(Event event) { + + if (!this.isEnabled()) { + return; + } + switch (event.getTopic()) { + + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: + this.handleBatteryState(); + break; + } + } + + private void handleBatteryState() { + switch (this.batteryState) { + case DEFAULT: + handleStateMachine(); + break; + case OFF: + stopSystem(); + break; + case ON: + startSystem(); + break; + case CONFIGURE: + System.out.println("Cluster cannot be configured currently!"); + break; + } + } + + private void handleStateMachine() { + log.info("Cluster.doNormalHandling(): State: " + this.getStateMachineState()); + boolean readyForWorking = false; + switch (this.getStateMachineState()) { + case ERROR: + stopSystem(); + errorDelayIsOver = LocalDateTime.now().plusSeconds(config.errorLevel2Delay()); + setStateMachineState(State.ERRORDELAY); + break; + case ERRORDELAY: + if (LocalDateTime.now().isAfter(errorDelayIsOver)) { + errorDelayIsOver = null; + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else { + this.setStateMachineState(State.OFF); + } + } + break; + case INIT: + if (this.isSystemRunning()) { + this.setStateMachineState(State.RUNNING); + unsuccessfulStarts = 0; + startAttemptTime = null; + } else { + if (startAttemptTime.plusSeconds(config.maxStartTime()).isBefore(LocalDateTime.now())) { + startAttemptTime = null; + unsuccessfulStarts++; + this.stopSystem(); + this.setStateMachineState(State.STOPPING); + if (unsuccessfulStarts >= config.maxStartAppempts()) { + errorDelayIsOver = LocalDateTime.now().plusSeconds(config.startUnsuccessfulDelay()); + this.setStateMachineState(State.ERRORDELAY); + unsuccessfulStarts = 0; + } + } + } + break; + case OFF: + this.startSystem(); + this.setStateMachineState(State.INIT); + startAttemptTime = LocalDateTime.now(); + break; + case RUNNING: + if (this.isSystemRunning()) { + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else { + readyForWorking = true; + } + } else { + this.setStateMachineState(State.UNDEFINED); + } + break; + case STOPPING: + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else { + if (this.isSystemStopped()) { + this.setStateMachineState(State.OFF); + } + } + break; + case UNDEFINED: + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else if (this.isSystemStopped()) { + this.setStateMachineState(State.OFF); + } else if (this.isSystemRunning()) { + this.setStateMachineState(State.RUNNING); + } else if (this.isSystemStatePending()) { + this.setStateMachineState(State.PENDING); + } + break; + case PENDING: + this.stopSystem(); + this.setStateMachineState(State.OFF); + break; + } + this.getReadyForWorking().setNextValue(readyForWorking); + } + + private boolean isError() { + // still TODO define what is exactly an error + if (readValueFromStateChannel(ClusterChannelId.MASTER_ALARM_LEVEL_2_INSULATION)) { + return true; + } + if (readValueFromStateChannel(ClusterChannelId.MASTER_ALARM_PCS_EMS_CONTROL_FAIL)) { + return true; + } + if (readValueFromStateChannel(ClusterChannelId.MASTER_ALARM_PCS_EMS_COMMUNICATION_FAILURE)) { + return true; + } + if (readValueFromStateChannel(ClusterChannelId.MASTER_ALARM_COMMUNICATION_ERROR_WITH_SUBMASTER)) { + return true; + } + + // Check for communication errors + for (int key : racks.keySet()) { + if (readValueFromStateChannel(RACK_INFO.get(key).subMasterCommunicationAlarmChannelId)) { + return true; + } + } + + return false; + } + + protected Channel addChannel(io.openems.edge.common.channel.ChannelId channelId) { + return super.addChannel(channelId); + } + + private boolean readValueFromStateChannel(io.openems.edge.common.channel.ChannelId channelId) { + StateChannel s = this.channel(channelId); + Optional val = s.value().asOptional(); + return val.isPresent() && val.get(); + } + + private boolean isSystemStopped() { + return haveAllRacksTheSameContactorControlState(ContactorControl.CUT_OFF); + } + + private boolean isSystemRunning() { + return haveAllRacksTheSameContactorControlState(ContactorControl.ON_GRID); + } + + private boolean haveAllRacksTheSameContactorControlState(ContactorControl cctrl) { + boolean b = true; + for (SingleRack r : racks.values()) { + b = b && cctrl == this.channel(RACK_INFO.get(r.getRackNumber()).positiveContactorChannelId).value() + .asEnum(); + } + return b; + } + + /** + * Checks whether system has an undefined state, e.g. rack 1 & 2 are configured, + * but only rack 1 is running. This state can only be reached at startup coming + * from state undefined + */ + private boolean isSystemStatePending() { + boolean b = true; + + for (SingleRack r : racks.values()) { + EnumReadChannel channel = this.channel(RACK_INFO.get(r.getRackNumber()).positiveContactorChannelId); + Optional val = channel.value().asOptional(); + b = b && val.isPresent(); + } + + return b && !isSystemRunning() && !isSystemStopped(); + } + + @Override + public String debugLog() { + return "SoC:" + this.getSoc().value() // + + "|Discharge:" + this.getDischargeMinVoltage().value() + ";" + this.getDischargeMaxCurrent().value() // + + "|Charge:" + this.getChargeMaxVoltage().value() + ";" + this.getChargeMaxCurrent().value(); + } + + private void startSystem() { + EnumWriteChannel startStopChannel = this.channel(ClusterChannelId.START_STOP); + try { + startStopChannel.setNextWriteValue(StartStop.START); + // Only set the racks that are used, but set the others to unused + for (int i : RACK_INFO.keySet()) { + EnumWriteChannel rackUsageChannel = this.channel(RACK_INFO.get(i).usageChannelId); + if (racks.containsKey(i)) { + rackUsageChannel.setNextWriteValue(RackUsage.USED); + } else { + rackUsageChannel.setNextWriteValue(RackUsage.UNUSED); + } + } + } catch (OpenemsNamedException e) { + log.error("Error while trying to start system\n" + e.getMessage()); + } + } + + private void stopSystem() { + EnumWriteChannel startStopChannel = this.channel(ClusterChannelId.START_STOP); + try { + startStopChannel.setNextWriteValue(StartStop.STOP); + // write to all racks unused!! + for (RackInfo r : RACK_INFO.values()) { + EnumWriteChannel rackUsageChannel = this.channel(r.usageChannelId); + rackUsageChannel.setNextWriteValue(RackUsage.UNUSED); + } + } catch (OpenemsNamedException e) { + log.error("Error while trying to stop system\n" + e.getMessage()); + } + } + + public String getModbusBridgeId() { + return modbusBridgeId; + } + + public State getStateMachineState() { + return state; + } + + public void setStateMachineState(State state) { + this.state = state; + this.channel(ClusterChannelId.STATE_MACHINE).setNextValue(this.state); + } + + @Override + protected ModbusProtocol defineModbusProtocol() { + ModbusProtocol protocol =new ModbusProtocol(this, new Task[] { + // -------- control registers of master -------------------------------------- + new FC16WriteRegistersTask(0x1017, // + m(ClusterChannelId.START_STOP, new UnsignedWordElement(0x1017)), // + m(ClusterChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // + m(ClusterChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // + m(ClusterChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)), // + m(ClusterChannelId.RACK_4_USAGE, new UnsignedWordElement(0x101B)), // + m(ClusterChannelId.RACK_5_USAGE, new UnsignedWordElement(0x101C)) // + ), // + new FC3ReadRegistersTask(0x1017, Priority.HIGH, + m(ClusterChannelId.START_STOP, new UnsignedWordElement(0x1017)), // + m(ClusterChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // + m(ClusterChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // + m(ClusterChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)), // + m(ClusterChannelId.RACK_4_USAGE, new UnsignedWordElement(0x101B)), // + m(ClusterChannelId.RACK_5_USAGE, new UnsignedWordElement(0x101C)) // + ), // + + new FC16WriteRegistersTask(0x101F, + m(ClusterChannelId.SYSTEM_INSULATION_LEVEL_1, new UnsignedWordElement(0x101F)), // + m(ClusterChannelId.SYSTEM_INSULATION_LEVEL_2, new UnsignedWordElement(0x1020)), // + new DummyRegisterElement(0x1021), // + m(ClusterChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x1022)), // + m(ClusterChannelId.EMS_ADDRESS, new UnsignedWordElement(0x1023)) // + ), // + + new FC3ReadRegistersTask(0x101F, Priority.LOW, + m(ClusterChannelId.SYSTEM_INSULATION_LEVEL_1, new UnsignedWordElement(0x101F)), // + m(ClusterChannelId.SYSTEM_INSULATION_LEVEL_2, new UnsignedWordElement(0x1020)), // + new DummyRegisterElement(0x1021), // + m(ClusterChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x1022)), // + m(ClusterChannelId.EMS_ADDRESS, new UnsignedWordElement(0x1023)) // + ), // + + new FC16WriteRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_1), + m(ClusterChannelId.RACK_1_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_1))) // + ), // + new FC3ReadRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_1), Priority.HIGH, + m(ClusterChannelId.RACK_1_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_1))) // + ), // + + new FC16WriteRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_2), + m(ClusterChannelId.RACK_2_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_2))) // + ), // + new FC3ReadRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_2), Priority.HIGH, + m(ClusterChannelId.RACK_2_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_2))) // + ), // + + new FC16WriteRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_3), + m(ClusterChannelId.RACK_2_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_3))) // + ), // + new FC3ReadRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_3), Priority.HIGH, + m(ClusterChannelId.RACK_3_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_3))) // + ), // + + new FC16WriteRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_4), + m(ClusterChannelId.RACK_4_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_4))) // + ), // + new FC3ReadRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_4), Priority.HIGH, + m(ClusterChannelId.RACK_4_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_4))) // + ), // + + new FC16WriteRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_5), + m(ClusterChannelId.RACK_5_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_5))) // + ), // + new FC3ReadRegistersTask(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_5), Priority.HIGH, + m(ClusterChannelId.RACK_5_POSITIVE_CONTACTOR, + new UnsignedWordElement(this.getAddressContactorControl(ADDRESS_OFFSET_RACK_5))) // + ), // + + // -------- state registers of master -------------------------------------- + new FC3ReadRegistersTask(0x1044, Priority.LOW, // + m(ClusterChannelId.CHARGE_INDICATION, new UnsignedWordElement(0x1044)), // + m(ClusterChannelId.SYSTEM_CURRENT, new UnsignedWordElement(0x1045), // + ElementToChannelConverter.SCALE_FACTOR_2), // TODO Check if scale factor is correct + new DummyRegisterElement(0x1046), // + m(Battery.ChannelId.SOC, new UnsignedWordElement(0x1047)) // + .onUpdateCallback( val -> { recalculateSoc(); } ), // + m(ClusterChannelId.SYSTEM_RUNNING_STATE, new UnsignedWordElement(0x1048)), // + m(ClusterChannelId.SYSTEM_VOLTAGE, new UnsignedWordElement(0x1049). // + onUpdateCallback( val -> { this.getVoltage().setNextValue(val/10); } ), // map value to api channel + ElementToChannelConverter.SCALE_FACTOR_2) // TODO Check if scale factor is correct + ), // + + new FC3ReadRegistersTask(0x104A, Priority.HIGH, // + m(ClusterChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x104A)), // + new DummyRegisterElement(0x104B, 0x104D), // + m(Battery.ChannelId.CHARGE_MAX_CURRENT, new UnsignedWordElement(0x104E), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(Battery.ChannelId.DISCHARGE_MAX_CURRENT, new UnsignedWordElement(0x104F), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + ), // + + new FC3ReadRegistersTask(0x1081, Priority.LOW, // + m(new BitsWordElement(0x1081, this) // + .bit(4, ClusterChannelId.MASTER_ALARM_LEVEL_2_INSULATION) // + .bit(3, ClusterChannelId.MASTER_ALARM_LEVEL_1_INSULATION) // + .bit(2, ClusterChannelId.MASTER_ALARM_PCS_EMS_CONTROL_FAIL) // + .bit(1, ClusterChannelId.MASTER_ALARM_PCS_EMS_COMMUNICATION_FAILURE) // + .bit(0, ClusterChannelId.MASTER_ALARM_COMMUNICATION_ERROR_WITH_SUBMASTER) // + ), // + m(new BitsWordElement(0x1082, this) // + .bit(4, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_5) // + .bit(3, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_4) // + .bit(2, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_3) // + .bit(1, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_2) // + .bit(0, ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_1) // + ), // + m(new BitsWordElement(0x1083, this) // + .bit(5, ClusterChannelId.RACK_1_LEVEL_2_ALARM) // + .bit(4, ClusterChannelId.RACK_1_PCS_CONTROL_FAULT) // + .bit(3, ClusterChannelId.RACK_1_COMMUNICATION_WITH_MASTER_ERROR) // + .bit(2, ClusterChannelId.RACK_1_DEVICE_ERROR) // + .bit(1, ClusterChannelId.RACK_1_CYCLE_OVER_CURRENT) // + .bit(0, ClusterChannelId.RACK_1_VOLTAGE_DIFFERENCE) // + ), // + m(new BitsWordElement(0x1084, this) // + .bit(5, ClusterChannelId.RACK_2_LEVEL_2_ALARM) // + .bit(4, ClusterChannelId.RACK_2_PCS_CONTROL_FAULT) // + .bit(3, ClusterChannelId.RACK_2_COMMUNICATION_WITH_MASTER_ERROR) // + .bit(2, ClusterChannelId.RACK_2_DEVICE_ERROR) // + .bit(1, ClusterChannelId.RACK_2_CYCLE_OVER_CURRENT) // + .bit(0, ClusterChannelId.RACK_2_VOLTAGE_DIFFERENCE) // + ), // + m(new BitsWordElement(0x1085, this) // + .bit(5, ClusterChannelId.RACK_3_LEVEL_2_ALARM) // + .bit(4, ClusterChannelId.RACK_3_PCS_CONTROL_FAULT) // + .bit(3, ClusterChannelId.RACK_3_COMMUNICATION_WITH_MASTER_ERROR) // + .bit(2, ClusterChannelId.RACK_3_DEVICE_ERROR) // + .bit(1, ClusterChannelId.RACK_3_CYCLE_OVER_CURRENT) // + .bit(0, ClusterChannelId.RACK_3_VOLTAGE_DIFFERENCE) // + ), // + m(new BitsWordElement(0x1086, this) // + .bit(5, ClusterChannelId.RACK_4_LEVEL_2_ALARM) // + .bit(4, ClusterChannelId.RACK_4_PCS_CONTROL_FAULT) // + .bit(3, ClusterChannelId.RACK_4_COMMUNICATION_WITH_MASTER_ERROR) // + .bit(2, ClusterChannelId.RACK_4_DEVICE_ERROR) // + .bit(1, ClusterChannelId.RACK_4_CYCLE_OVER_CURRENT) // + .bit(0, ClusterChannelId.RACK_4_VOLTAGE_DIFFERENCE) // + ), // + m(new BitsWordElement(0x1087, this) // + .bit(5, ClusterChannelId.RACK_5_LEVEL_2_ALARM) // + .bit(4, ClusterChannelId.RACK_5_PCS_CONTROL_FAULT) // + .bit(3, ClusterChannelId.RACK_5_COMMUNICATION_WITH_MASTER_ERROR) // + .bit(2, ClusterChannelId.RACK_5_DEVICE_ERROR) // + .bit(1, ClusterChannelId.RACK_5_CYCLE_OVER_CURRENT) // + .bit(0, ClusterChannelId.RACK_5_VOLTAGE_DIFFERENCE) // + ) // + ) // + + + }); + + for (SingleRack rack : racks.values()) { + protocol.addTasks(rack.getTasks().toArray(new Task[] {})); + } + + return protocol; + } + + private int getAddressContactorControl(int addressOffsetRack) { + return addressOffsetRack + OFFSET_CONTACTOR_CONTROL; + } + + protected final AbstractModbusElement map(io.openems.edge.common.channel.ChannelId channelId, + AbstractModbusElement element) { + return this.m(channelId, element); + } + + protected final AbstractModbusElement map(io.openems.edge.common.channel.ChannelId channelId, + AbstractModbusElement element, ElementToChannelConverter converter) { + return this.m(channelId, element, converter); + } + + protected final AbstractModbusElement map(BitsWordElement bitsWordElement) { + return super.m(bitsWordElement); + } + + public void recalculateSoc() { + int i = 0; + int soc = 0; + + for (SingleRack rack : this.racks.values()) { + this.log.info("Rack " + rack.getRackNumber() + " has a SoC of " + rack.getSoC() + " %!"); + soc = soc + rack.getSoC(); + i++; + } + + if (i > 0) { + soc = soc / i; + } + + this.log.info("Calculated SoC: " + soc); + + this.channel(Battery.ChannelId.SOC).setNextValue(soc); + } + + private static Map createRackInfo() { + Map map = new HashMap(); + map.put(1, + new RackInfo(ADDRESS_OFFSET_RACK_1, ClusterChannelId.RACK_1_USAGE, + ClusterChannelId.RACK_1_POSITIVE_CONTACTOR, + ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_1)); + map.put(2, + new RackInfo(ADDRESS_OFFSET_RACK_2, ClusterChannelId.RACK_2_USAGE, + ClusterChannelId.RACK_2_POSITIVE_CONTACTOR, + ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_2)); + map.put(3, + new RackInfo(ADDRESS_OFFSET_RACK_3, ClusterChannelId.RACK_3_USAGE, + ClusterChannelId.RACK_3_POSITIVE_CONTACTOR, + ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_3)); + map.put(4, + new RackInfo(ADDRESS_OFFSET_RACK_4, ClusterChannelId.RACK_4_USAGE, + ClusterChannelId.RACK_4_POSITIVE_CONTACTOR, + ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_4)); + map.put(5, + new RackInfo(ADDRESS_OFFSET_RACK_5, ClusterChannelId.RACK_5_USAGE, + ClusterChannelId.RACK_5_POSITIVE_CONTACTOR, + ClusterChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_5)); + + return map; + } + + // Helper class to get infos about connected racks + private static class RackInfo { + int addressOffset; + ClusterChannelId usageChannelId; + ClusterChannelId positiveContactorChannelId; + ClusterChannelId subMasterCommunicationAlarmChannelId; + + RackInfo( // + int addressOffset, // + ClusterChannelId usageChannelId, // + ClusterChannelId positiveContactorChannelId, // + ClusterChannelId subMasterCommunicationAlarmChannelId // + ) { + this.addressOffset = addressOffset; + this.usageChannelId = usageChannelId; + this.subMasterCommunicationAlarmChannelId = subMasterCommunicationAlarmChannelId; + this.positiveContactorChannelId = positiveContactorChannelId; + } + } +} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/MultiRackChannelId.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/ClusterChannelId.java similarity index 81% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/MultiRackChannelId.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/ClusterChannelId.java index 1527174f567..2cd59d99ae7 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/MultiRackChannelId.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/ClusterChannelId.java @@ -1,12 +1,13 @@ -package io.openems.edge.battery.soltaro.multirack; +package io.openems.edge.battery.soltaro.cluster.versionb; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; +import io.openems.edge.battery.soltaro.State; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; -public enum MultiRackChannelId implements io.openems.edge.common.channel.ChannelId { +public enum ClusterChannelId implements io.openems.edge.common.channel.ChannelId { // EnumReadChannels STATE_MACHINE(Doc.of(State.values()) // .text("Current State of State-Machine")), // @@ -34,20 +35,15 @@ public enum MultiRackChannelId implements io.openems.edge.common.channel.Channel EMS_COMMUNICATION_TIMEOUT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.SECONDS) // .accessMode(AccessMode.READ_WRITE)), // - RACK_1_POSITIVE_CONTACTOR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE) // + RACK_1_POSITIVE_CONTACTOR(Doc.of(Enums.ContactorControl.values()) // .accessMode(AccessMode.READ_WRITE)), // - RACK_2_POSITIVE_CONTACTOR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE) // + RACK_2_POSITIVE_CONTACTOR(Doc.of(Enums.ContactorControl.values()) // .accessMode(AccessMode.READ_WRITE)), // - RACK_3_POSITIVE_CONTACTOR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE) // + RACK_3_POSITIVE_CONTACTOR(Doc.of(Enums.ContactorControl.values()) // .accessMode(AccessMode.READ_WRITE)), // - RACK_4_POSITIVE_CONTACTOR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE) // + RACK_4_POSITIVE_CONTACTOR(Doc.of(Enums.ContactorControl.values()) // .accessMode(AccessMode.READ_WRITE)), // - RACK_5_POSITIVE_CONTACTOR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE) // + RACK_5_POSITIVE_CONTACTOR(Doc.of(Enums.ContactorControl.values()) // .accessMode(AccessMode.READ_WRITE)), // SYSTEM_INSULATION_LEVEL_1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.OHM) // @@ -57,9 +53,9 @@ public enum MultiRackChannelId implements io.openems.edge.common.channel.Channel .accessMode(AccessMode.READ_WRITE)), // // IntegerReadChannels - CURRENT(Doc.of(OpenemsType.INTEGER) // + SYSTEM_CURRENT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIAMPERE)), // - VOLTAGE(Doc.of(OpenemsType.INTEGER) // + SYSTEM_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT)), // SYSTEM_INSULATION(Doc.of(OpenemsType.INTEGER) // .unit(Unit.OHM)), // @@ -75,15 +71,15 @@ public enum MultiRackChannelId implements io.openems.edge.common.channel.Channel .text("System insulation alarm level 1")), MASTER_ALARM_LEVEL_2_INSULATION(Doc.of(Level.FAULT) // .text("System insulation alarm level 2")), - SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_1(Doc.of(Level.FAULT) // + SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_1(Doc.of(Level.OK) // .text("Communication to sub master 1 fault")), - SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_2(Doc.of(Level.FAULT) // + SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_2(Doc.of(Level.OK) // .text("Communication to sub master 2 fault")), - SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_3(Doc.of(Level.FAULT) // + SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_3(Doc.of(Level.OK) // .text("Communication to sub master 3 fault")), - SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_4(Doc.of(Level.FAULT) // + SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_4(Doc.of(Level.OK) // .text("Communication to sub master 4 fault")), - SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_5(Doc.of(Level.FAULT) // + SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_5(Doc.of(Level.OK) // .text("Communication to sub master 5 fault")), RACK_1_LEVEL_2_ALARM(Doc.of(Level.FAULT) // .text("Rack 1 Level 2 Alarm")), @@ -148,11 +144,12 @@ public enum MultiRackChannelId implements io.openems.edge.common.channel.Channel RACK_5_CYCLE_OVER_CURRENT(Doc.of(Level.FAULT) // .text("Rack 1 Cycle over current")), RACK_5_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) // - .text("Rack 1 Voltage difference")); + .text("Rack 1 Voltage difference")) + ; private final Doc doc; - private MultiRackChannelId(Doc doc) { + private ClusterChannelId(Doc doc) { this.doc = doc; } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/Config.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Config.java similarity index 64% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/Config.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Config.java index 4dd47f14610..aed624bd3de 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/Config.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Config.java @@ -1,4 +1,4 @@ -package io.openems.edge.battery.soltaro.multirack; +package io.openems.edge.battery.soltaro.cluster.versionb; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @@ -6,60 +6,51 @@ import io.openems.edge.battery.soltaro.BatteryState; @ObjectClassDefinition( // - name = "BMS FENECON Soltaro MultiRack", // + name = "BMS Soltaro Cluster Version B", // description = "Implements the Soltaro multi rack battery system.") @interface Config { - String service_pid(); + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "bms0"; + @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 = "Modbus-ID", description = "ID of Modbus brige; ! Soltaro MultiRack needs baudrate of 57600 !") - String modbus_id(); - + @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus brige; ! Soltaro Cluster needs baudrate of 57600 !") + String modbus_id() default "modbus0"; + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") int modbusUnitId() default 0; - + @AttributeDefinition(name = "Battery state", description = "Switches the battery into the given state, if default is used, battery state is set automatically") BatteryState batteryState() default BatteryState.DEFAULT; - - @AttributeDefinition(name = "Number of slaves", description = "The number of slave modules per rack (max. 22)", min = "1", max = "22") + + @AttributeDefinition(name = "Number of slaves", description = "The number of slaves in this battery rack (max. 20)", min = "1", max = "20") int numberOfSlaves() default 20; - - // TODO much more comfortable + @AttributeDefinition(name = "Racks Used", description = "Determines what racks are used") int[] racks(); - -// @AttributeDefinition(name = "Rack 1 Usage", description = "Determines whether rack 1 is used") -// boolean rack1IsUsed() default true; -// -// @AttributeDefinition(name = "Rack 2 Usage", description = "Determines whether rack 2 is used") -// boolean rack2IsUsed() default true; -// -// @AttributeDefinition(name = "Rack 3 Usage", description = "Determines whether rack 3 is used") -// boolean rack3IsUsed() default true; -// -// @AttributeDefinition(name = "Rack 4 Usage", description = "Determines whether rack 4 is used") -// boolean rack4IsUsed() default true; -// -// @AttributeDefinition(name = "Rack 5 Usage", description = "Determines whether rack 5 is used") -// boolean rack5IsUsed() default true; - + @AttributeDefinition(name = "Error Level 2 Delay", description = "Sets the delay time in seconds how long the system should be stopped after an error level 2 has occurred") int errorLevel2Delay() default 600; - + @AttributeDefinition(name = "Max Start Attempts", description = "Sets the counter how many time the system should try to start") int maxStartAppempts() default 5; - + @AttributeDefinition(name = "Max Start Time", description = "Max Time in seconds allowed for starting the system") int maxStartTime() default 20; - + @AttributeDefinition(name = "Start Not Successful Delay Time", description = "Sets the delay time in seconds how long the system should be stopped if it was not able to start") int startUnsuccessfulDelay() default 3600; + @AttributeDefinition(name = "Minimal Cell Voltage Millivolt", description = "Minimal cell voltage in milli volt when system does not allow further discharging") + int minimalCellVoltage() default 2800; + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "BMS FENECON Soltaro MultiRack [{id}]"; + String webconsole_configurationFactory_nameHint() default "BMS Soltaro Cluster Version B [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/Enums.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Enums.java similarity index 96% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/Enums.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Enums.java index c7b791979dc..6c27581d89a 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/Enums.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/Enums.java @@ -1,6 +1,6 @@ -package io.openems.edge.battery.soltaro.multirack; +package io.openems.edge.battery.soltaro.cluster.versionb; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public class Enums { diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/SingleRack.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/SingleRack.java similarity index 81% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/SingleRack.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/SingleRack.java index b02e6142799..a4f39f90f96 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/SingleRack.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/SingleRack.java @@ -1,12 +1,19 @@ -package io.openems.edge.battery.soltaro.multirack; +package io.openems.edge.battery.soltaro.cluster.versionb; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; +import io.openems.edge.battery.soltaro.ChannelIdImpl; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.SignedWordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; @@ -15,8 +22,6 @@ import io.openems.edge.common.channel.ChannelId; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.IntegerDoc; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.taskmanager.Priority; /** @@ -95,11 +100,11 @@ public class SingleRack { private int rackNumber; private int numberOfSlaves; private int addressOffset; - private MultiRack parent; + private Cluster parent; private final Map channelIds; private final Map> channelMap; - protected SingleRack(int racknumber, int numberOfSlaves, int addressOffset, MultiRack parent) { + protected SingleRack(int racknumber, int numberOfSlaves, int addressOffset, Cluster parent) { this.rackNumber = racknumber; this.numberOfSlaves = numberOfSlaves; this.addressOffset = addressOffset; @@ -111,6 +116,16 @@ protected SingleRack(int racknumber, int numberOfSlaves, int addressOffset, Mult public Collection> getChannels() { return channelMap.values(); } + + public int getSoC() { + @SuppressWarnings("unchecked") + Optional socOpt = (Optional) this.channelMap.get(KEY_SOC).value().asOptional(); + int soc = 0; + if (socOpt.isPresent()) { + soc = socOpt.get(); + } + return soc; + } private Map> createChannelMap() { Map> channels = new HashMap<>(); @@ -314,8 +329,7 @@ private Map createChannelIdMap() { // 10 addEntry(map, KEY_FAILURE_TEMPERATURE_SENSOR_CABLE, Doc.of(Level.FAULT).text("Temperature sensor cable fault")); // Bit // 9 - addEntry(map, KEY_FAILURE_BALANCING_MODULE, Doc.of(Level.FAULT).text("Balancing module fault")); // Bit - // 8 + addEntry(map, KEY_FAILURE_BALANCING_MODULE, Doc.of(Level.OK).text("Balancing module fault")); // Bit 8 addEntry(map, KEY_FAILURE_TEMPERATURE_PCB, Doc.of(Level.FAULT).text("Temperature PCB error")); // Bit 7 addEntry(map, KEY_FAILURE_GR_TEMPERATURE, Doc.of(Level.FAULT).text("GR Temperature error")); // Bit 6 addEntry(map, KEY_FAILURE_TEMP_SENSOR, Doc.of(Level.FAULT).text("Temperature sensor fault")); // Bit 5 @@ -353,7 +367,8 @@ public Collection getTasks() { parent.map(channelIds.get(KEY_VOLTAGE), getUWE(0x100), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // parent.map(channelIds.get(KEY_CURRENT), getUWE(0x101), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // parent.map(channelIds.get(KEY_CHARGE_INDICATION), getUWE(0x102)), // - parent.map(channelIds.get(KEY_SOC), getUWE(0x103)), // + parent.map(channelIds.get(KEY_SOC), getUWE(0x103)). // + onUpdateCallback( val -> { parent.recalculateSoc(); } ), // parent.map(channelIds.get(KEY_SOH), getUWE(0x104)), // parent.map(channelIds.get(KEY_MAX_CELL_VOLTAGE_ID), getUWE(0x105)), // parent.map(channelIds.get(KEY_MAX_CELL_VOLTAGE), getUWE(0x106)), // @@ -367,85 +382,102 @@ public Collection getTasks() { // Alarm levels tasks.add(new FC3ReadRegistersTask(this.addressOffset + 0x140, Priority.LOW, // - parent.map(getUWE(0x140)) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH), 0) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH), 1) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_CHA_CURRENT_HIGH), 2) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_CELL_VOLTAGE_LOW), 3) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW), 4) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH), 5) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH), 6) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW), 7) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_GR_TEMPERATURE_HIGH), 10) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH), 14) // - .m(channelIds.get(KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW), 15) // - .build(), // - parent.map(getUWE(0x141)) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH), 0) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH), 1) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CHA_CURRENT_HIGH), 2) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_VOLTAGE_LOW), 3) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW), 4) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH), 5) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH), 6) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW), 7) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_SOC_LOW), 8) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH), 9) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_GR_TEMPERATURE_HIGH), 10) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH), 11) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH), 13) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH), 14) // - .m(channelIds.get(KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW), 15) // - .build(), // + parent.map(getBWE(0x140, parent) // + .bit(0, channelIds.get(KEY_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH)) // + .bit(1, channelIds.get(KEY_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH)) // + .bit(2, channelIds.get(KEY_ALARM_LEVEL_2_CHA_CURRENT_HIGH)) // + .bit(3, channelIds.get(KEY_ALARM_LEVEL_2_CELL_VOLTAGE_LOW)) // + .bit(4, channelIds.get(KEY_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW)) // + .bit(5, channelIds.get(KEY_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH)) // + .bit(6, channelIds.get(KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH)) // + .bit(7, channelIds.get(KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW)) // + .bit(10, channelIds.get(KEY_ALARM_LEVEL_2_GR_TEMPERATURE_HIGH)) // + .bit(14, channelIds.get(KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH)) // + .bit(15, channelIds.get(KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)) // + ), // + parent.map(getBWE(0x141, parent) // + .bit(0, channelIds.get(KEY_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH)) // + .bit(1, channelIds.get(KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH)) // + .bit(2, channelIds.get(KEY_ALARM_LEVEL_1_CHA_CURRENT_HIGH)) // + .bit(3, channelIds.get(KEY_ALARM_LEVEL_1_CELL_VOLTAGE_LOW)) // + .bit(4, channelIds.get(KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW)) // + .bit(5, channelIds.get(KEY_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH)) // + .bit(6, channelIds.get(KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH)) // + .bit(7, channelIds.get(KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW)) // + .bit(8, channelIds.get(KEY_ALARM_LEVEL_1_SOC_LOW)) // + .bit(9, channelIds.get(KEY_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH)) // + .bit(10, channelIds.get(KEY_ALARM_LEVEL_1_GR_TEMPERATURE_HIGH)) // + .bit(11, channelIds.get(KEY_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH)) // + .bit(13, channelIds.get(KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH)) // + .bit(14, channelIds.get(KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH)) // + .bit(15, channelIds.get(KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW)) // + ), // parent.map(channelIds.get(KEY_RUN_STATE), getUWE(0x142)) // ) // ); // Error Codes tasks.add(new FC3ReadRegistersTask(this.addressOffset + 0x185, Priority.LOW, // - parent.map(getUWE(0x185)) // - .m(channelIds.get(KEY_FAILURE_SAMPLING_WIRE), 0)// - .m(channelIds.get(KEY_FAILURE_CONNECTOR_WIRE), 1)// - .m(channelIds.get(KEY_FAILURE_LTC6803), 2)// - .m(channelIds.get(KEY_FAILURE_VOLTAGE_SAMPLING), 3)// - .m(channelIds.get(KEY_FAILURE_TEMP_SAMPLING), 4)// - .m(channelIds.get(KEY_FAILURE_TEMP_SENSOR), 5)// - .m(channelIds.get(KEY_FAILURE_GR_TEMPERATURE), 6)// - .m(channelIds.get(KEY_FAILURE_TEMPERATURE_PCB), 7)// - .m(channelIds.get(KEY_FAILURE_BALANCING_MODULE), 8)// - .m(channelIds.get(KEY_FAILURE_TEMPERATURE_SENSOR_CABLE), 9)// - .m(channelIds.get(KEY_FAILURE_INTRANET_COMMUNICATION), 10)// - .m(channelIds.get(KEY_FAILURE_EEPROM), 11)// - .m(channelIds.get(KEY_FAILURE_INITIALIZATION), 12)// - .build() // + parent.map(getBWE(0x185, parent) // + .bit(0, channelIds.get(KEY_FAILURE_SAMPLING_WIRE))// + .bit(1, channelIds.get(KEY_FAILURE_CONNECTOR_WIRE))// + .bit(2, channelIds.get(KEY_FAILURE_LTC6803))// + .bit(3, channelIds.get(KEY_FAILURE_VOLTAGE_SAMPLING))// + .bit(4, channelIds.get(KEY_FAILURE_TEMP_SAMPLING))// + .bit(5, channelIds.get(KEY_FAILURE_TEMP_SENSOR))// + .bit(6, channelIds.get(KEY_FAILURE_GR_TEMPERATURE))// + .bit(7, channelIds.get(KEY_FAILURE_TEMPERATURE_PCB))// + .bit(8, channelIds.get(KEY_FAILURE_BALANCING_MODULE))// + .bit(9, channelIds.get(KEY_FAILURE_TEMPERATURE_SENSOR_CABLE))// + .bit(10, channelIds.get(KEY_FAILURE_INTRANET_COMMUNICATION))// + .bit(11, channelIds.get(KEY_FAILURE_EEPROM))// + .bit(12, channelIds.get(KEY_FAILURE_INITIALIZATION))// + ) // )); + int MAX_ELEMENTS_PER_TASK = 100; + // Cell voltages for (int i = 0; i < this.numberOfSlaves; i++) { - Collection> elements = new ArrayList<>(); + List> elements = new ArrayList<>(); for (int j = i * VOLTAGE_SENSORS_PER_MODULE; j < (i + 1) * VOLTAGE_SENSORS_PER_MODULE; j++) { String key = getSingleCellPrefix(j) + "_" + VOLTAGE; UnsignedWordElement uwe = getUWE(VOLTAGE_ADDRESS_OFFSET + j); AbstractModbusElement ame = parent.map(channelIds.get(key), uwe); elements.add(ame); } - tasks.add(new FC3ReadRegistersTask( - this.addressOffset + VOLTAGE_ADDRESS_OFFSET + i * VOLTAGE_SENSORS_PER_MODULE, Priority.LOW, - elements.toArray(new AbstractModbusElement[0]))); + + //not more than 100 elements per task, because it can cause problems.. + int taskCount = (elements.size() / MAX_ELEMENTS_PER_TASK) + 1; + + for (int x = 0; x < taskCount; x++) { + List> subElements = elements.subList( x * MAX_ELEMENTS_PER_TASK , Math.min( ((x + 1) * MAX_ELEMENTS_PER_TASK ), elements.size() ) ); + AbstractModbusElement[] taskElements = subElements.toArray(new AbstractModbusElement[0]); + tasks.add(new FC3ReadRegistersTask(taskElements[0].getStartAddress(), Priority.LOW, taskElements)); + } + + } // Cell temperatures for (int i = 0; i < this.numberOfSlaves; i++) { - Collection> elements = new ArrayList<>(); + List> elements = new ArrayList<>(); for (int j = i * TEMPERATURE_SENSORS_PER_MODULE; j < (i + 1) * TEMPERATURE_SENSORS_PER_MODULE; j++) { String key = getSingleCellPrefix(j) + "_" + TEMPERATURE; - SignedWordElement swe = new SignedWordElement(this.addressOffset + TEMPERATURE_ADDRESS_OFFSET + j); + + SignedWordElement swe = getSWE(TEMPERATURE_ADDRESS_OFFSET + j); AbstractModbusElement ame = parent.map(channelIds.get(key), swe); elements.add(ame); } - tasks.add(new FC3ReadRegistersTask( - this.addressOffset + TEMPERATURE_ADDRESS_OFFSET + i * TEMPERATURE_SENSORS_PER_MODULE, Priority.LOW, - elements.toArray(new AbstractModbusElement[0]))); + + //not more than 100 elements per task, because it can cause problems.. + int taskCount = (elements.size() / MAX_ELEMENTS_PER_TASK) + 1; + + for (int x = 0; x < taskCount; x++) { + List> subElements = elements.subList( x * MAX_ELEMENTS_PER_TASK , Math.min( ((x + 1) * MAX_ELEMENTS_PER_TASK ), elements.size() ) ); + AbstractModbusElement[] taskElements = subElements.toArray(new AbstractModbusElement[0]); + tasks.add(new FC3ReadRegistersTask(taskElements[0].getStartAddress(), Priority.LOW, taskElements)); + } } return tasks; @@ -471,7 +503,15 @@ private String getSingleCellPrefix(int num) { return RACK + "_" + this.rackNumber + "_" + BATTERY + "_" + String.format(NUMBER_FORMAT, num); } - private UnsignedWordElement getUWE(int address) { - return new UnsignedWordElement(this.addressOffset + address); + private BitsWordElement getBWE(int addressWithoutOffset, AbstractOpenemsModbusComponent component) { + return new BitsWordElement(this.addressOffset + addressWithoutOffset, component); + } + + private UnsignedWordElement getUWE(int addressWithoutOffset) { + return new UnsignedWordElement(this.addressOffset + addressWithoutOffset); + } + + private SignedWordElement getSWE(int addressWithoutOffset) { + return new SignedWordElement(this.addressOffset + addressWithoutOffset); } } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/package-info.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/package-info.java new file mode 100644 index 00000000000..7c3d457849b --- /dev/null +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.edge.battery.soltaro.cluster.versionb; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/package-info.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/package-info.java deleted file mode 100644 index 8a02719035c..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.edge.battery.soltaro.master; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/ChannelIdImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/ChannelIdImpl.java deleted file mode 100644 index ce8ffabdf6d..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/ChannelIdImpl.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.openems.edge.battery.soltaro.multirack; - -import io.openems.edge.common.channel.ChannelId; -import io.openems.edge.common.channel.Doc; - -public class ChannelIdImpl implements ChannelId { - - private final String name; - private final Doc doc; - - public ChannelIdImpl(String name, Doc doc) { - this.name = name; - this.doc = doc; - } - - @Override - public String name() { - return this.name; - } - - @Override - public Doc doc() { - return this.doc; - } -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/MultiRack.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/MultiRack.java deleted file mode 100644 index c46095b268e..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/MultiRack.java +++ /dev/null @@ -1,553 +0,0 @@ -package io.openems.edge.battery.soltaro.multirack; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -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.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.event.Event; -import org.osgi.service.event.EventConstants; -import org.osgi.service.event.EventHandler; -import org.osgi.service.metatype.annotations.Designate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.exceptions.OpenemsException; -import io.openems.edge.battery.api.Battery; -import io.openems.edge.battery.soltaro.BatteryState; -import io.openems.edge.battery.soltaro.multirack.Enums.ContactorControl; -import io.openems.edge.battery.soltaro.multirack.Enums.RackUsage; -import io.openems.edge.battery.soltaro.multirack.Enums.StartStop; -import io.openems.edge.battery.soltaro.versionb.ModuleParameters; -import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; -import io.openems.edge.bridge.modbus.api.BridgeModbus; -import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; -import io.openems.edge.bridge.modbus.api.ModbusProtocol; -import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; -import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; -import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; -import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; -import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; -import io.openems.edge.bridge.modbus.api.task.Task; -import io.openems.edge.common.channel.Channel; -import io.openems.edge.common.channel.IntegerReadChannel; -import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.StateChannel; -import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.event.EdgeEventConstants; -import io.openems.edge.common.taskmanager.Priority; - -@Designate(ocd = Config.class, factory = true) -@Component( // - name = "Bms.Fenecon.MultiRack", // - immediate = true, // - configurationPolicy = ConfigurationPolicy.REQUIRE, // - property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // -) -public class MultiRack extends AbstractOpenemsModbusComponent implements Battery, OpenemsComponent, EventHandler { - - public static final int DISCHARGE_MAX_A = 0; // default value 0 to avoid damages - public static final int CHARGE_MAX_A = 0; // default value 0 to avoid damages - - private static final int ADDRESS_OFFSET_RACK_1 = 0x2000; - private static final int ADDRESS_OFFSET_RACK_2 = 0x3000; - private static final int ADDRESS_OFFSET_RACK_3 = 0x4000; - private static final int ADDRESS_OFFSET_RACK_4 = 0x5000; - private static final int ADDRESS_OFFSET_RACK_5 = 0x6000; - - private static final Map RACK_INFO = createRackInfo(); - private final Logger log = LoggerFactory.getLogger(MultiRack.class); - - @Reference - protected ConfigurationAdmin cm; - - // If an error has occurred, this indicates the time when next action could be - // done - private LocalDateTime errorDelayIsOver = null; - private int unsuccessfulStarts = 0; - private LocalDateTime startAttemptTime = null; - private String modbusBridgeId; - private BatteryState batteryState; - private State state = State.UNDEFINED; - private Config config; - private Collection racks = new ArrayList<>(); - - public MultiRack() { - super(// - OpenemsComponent.ChannelId.values(), // - Battery.ChannelId.values(), // - MultiRackChannelId.values() // - ); - } - - @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) - protected void setModbus(BridgeModbus modbus) { - super.setModbus(modbus); - } - - @Activate - void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", - config.modbus_id()); - - this.config = config; - this.modbusBridgeId = config.modbus_id(); - this.batteryState = config.batteryState(); - - // Create racks dynamically - for (int i : config.racks()) { - this.racks.add(new SingleRack(i, config.numberOfSlaves(), RACK_INFO.get(i).addressOffset, this)); - } - - this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(MultiRack.CHARGE_MAX_A); - this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(MultiRack.DISCHARGE_MAX_A); - this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE) - .setNextValue(this.config.numberOfSlaves() * ModuleParameters.MAX_VOLTAGE_MILLIVOLT.getValue() / 1000); - this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE) - .setNextValue(this.config.numberOfSlaves() * ModuleParameters.MIN_VOLTAGE_MILLIVOLT.getValue() / 1000); - this.channel(Battery.ChannelId.CAPACITY).setNextValue(this.config.racks().length * this.config.numberOfSlaves() - * ModuleParameters.CAPACITY_WH.getValue() / 1000); - } - - @Override - public void handleEvent(Event event) { - - if (!this.isEnabled()) { - return; - } - switch (event.getTopic()) { - - case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: - this.handleBatteryState(); - break; - } - } - - private void handleBatteryState() { - switch (this.batteryState) { - case DEFAULT: - handleStateMachine(); - break; - case OFF: - stopSystem(); - break; - case ON: - startSystem(); - break; - } - } - - private void handleStateMachine() { - log.info("MultiRack.doNormalHandling(): State: " + this.getStateMachineState()); - boolean readyForWorking = false; - switch (this.getStateMachineState()) { - case ERROR: - stopSystem(); - errorDelayIsOver = LocalDateTime.now().plusSeconds(config.errorLevel2Delay()); - setStateMachineState(State.ERRORDELAY); - break; - case ERRORDELAY: - if (LocalDateTime.now().isAfter(errorDelayIsOver)) { - errorDelayIsOver = null; - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else { - this.setStateMachineState(State.OFF); - } - } - break; - case INIT: - if (this.isSystemRunning()) { - this.setStateMachineState(State.RUNNING); - unsuccessfulStarts = 0; - startAttemptTime = null; - } else { - if (startAttemptTime.plusSeconds(config.maxStartTime()).isBefore(LocalDateTime.now())) { - startAttemptTime = null; - unsuccessfulStarts++; - this.stopSystem(); - this.setStateMachineState(State.STOPPING); - if (unsuccessfulStarts >= config.maxStartAppempts()) { - errorDelayIsOver = LocalDateTime.now().plusSeconds(config.startUnsuccessfulDelay()); - this.setStateMachineState(State.ERRORDELAY); - unsuccessfulStarts = 0; - } - } - } - break; - case OFF: - this.startSystem(); - this.setStateMachineState(State.INIT); - startAttemptTime = LocalDateTime.now(); - break; - case RUNNING: - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else { - readyForWorking = true; - } - break; - case STOPPING: - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else { - if (this.isSystemStopped()) { - this.setStateMachineState(State.OFF); - } - } - break; - case UNDEFINED: - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else if (this.isSystemStopped()) { - this.setStateMachineState(State.OFF); - } else if (this.isSystemRunning()) { - this.setStateMachineState(State.RUNNING); - } else if (this.isSystemStatePending()) { - this.setStateMachineState(State.PENDING); - } - break; - case PENDING: - this.stopSystem(); - this.setStateMachineState(State.OFF); - break; - } - this.getReadyForWorking().setNextValue(readyForWorking); - } - - private boolean isError() { - // TODO define what is an error - // should we look at the submasters for level 2 errors? - if (readValueFromStateChannel(MultiRackChannelId.MASTER_ALARM_LEVEL_2_INSULATION)) { - return true; - } - if (readValueFromStateChannel(MultiRackChannelId.MASTER_ALARM_PCS_EMS_CONTROL_FAIL)) { - return true; - } - if (readValueFromStateChannel(MultiRackChannelId.MASTER_ALARM_PCS_EMS_COMMUNICATION_FAILURE)) { - return true; - } - if (readValueFromStateChannel(MultiRackChannelId.MASTER_ALARM_COMMUNICATION_ERROR_WITH_SUBMASTER)) { - return true; - } - - return false; - } - - public Channel addChannel(io.openems.edge.common.channel.ChannelId channelId) { - return this.addChannel(channelId); - } - - private boolean readValueFromStateChannel(io.openems.edge.common.channel.ChannelId channelId) { - StateChannel s = this.channel(channelId); - Optional val = s.value().asOptional(); - return val.isPresent() && val.get(); - } - - private boolean isSystemStopped() { - boolean ret = true; - - for (SingleRack rack : racks) { - ret = ret && ContactorControl.CUT_OFF == this - .channel(RACK_INFO.get(rack.getRackNumber()).positiveContactorChannelId).value().asEnum(); - } - - return ret; - } - - private boolean isSystemRunning() { - boolean ret = true; - - for (SingleRack rack : racks) { - ret = ret && ContactorControl.ON_GRID == this - .channel(RACK_INFO.get(rack.getRackNumber()).positiveContactorChannelId).value().asEnum(); - } - - return ret; - } - - /** - * Checks whether system has an undefined state, e.g. rack 1 & 2 are configured, - * but only rack 1 is running. This state can only be reached at startup coming - * from state undefined - */ - private boolean isSystemStatePending() { - boolean ret = true; - - for (SingleRack rack : racks) { - IntegerReadChannel channel = this.channel(RACK_INFO.get(rack.getRackNumber()).positiveContactorChannelId); - Optional val = channel.value().asOptional(); - ret = ret && val.isPresent(); - } - - return ret && !isSystemRunning() && !isSystemStopped(); - } - - @Override - public String debugLog() { - return "SoC:" + this.getSoc().value() // - + "|Discharge:" + this.getDischargeMinVoltage().value() + ";" + this.getDischargeMaxCurrent().value() // - + "|Charge:" + this.getChargeMaxVoltage().value() + ";" + this.getChargeMaxCurrent().value(); - } - - private void startSystem() { - IntegerWriteChannel startStopChannel = this.channel(MultiRackChannelId.START_STOP); - try { - startStopChannel.setNextWriteValue(StartStop.START.getValue()); -// TODO write to all racks unused!! - for (SingleRack r : racks) { - IntegerWriteChannel rackUsageChannel = this.channel(RACK_INFO.get(r.getRackNumber()).usageChannelId); - rackUsageChannel.setNextWriteValue(RackUsage.USED.getValue()); - } - } catch (OpenemsException e) { - log.error("Error while trying to start system\n" + e.getMessage()); - } - } - - private void stopSystem() { - - IntegerWriteChannel startStopChannel = this.channel(MultiRackChannelId.START_STOP); - try { - startStopChannel.setNextWriteValue(StartStop.STOP.getValue()); -// TODO write to all racks unused!! - for (SingleRack r : racks) { - IntegerWriteChannel rackUsageChannel = this.channel(RACK_INFO.get(r.getRackNumber()).usageChannelId); - rackUsageChannel.setNextWriteValue(RackUsage.UNUSED.getValue()); - } - } catch (OpenemsException e) { - log.error("Error while trying to stop system\n" + e.getMessage()); - } - } - - public String getModbusBridgeId() { - return modbusBridgeId; - } - - public State getStateMachineState() { - return state; - } - - public void setStateMachineState(State state) { - this.state = state; - this.channel(MultiRackChannelId.STATE_MACHINE).setNextValue(this.state); - } - - @Override - protected ModbusProtocol defineModbusProtocol() { - Collection tasks = new ArrayList<>(); - tasks.addAll(Arrays.asList(new Task[] { - // -------- control registers of master -------------------------------------- - new FC16WriteRegistersTask(0x1017, m(MultiRackChannelId.START_STOP, new UnsignedWordElement(0x1017)), // - m(MultiRackChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // - m(MultiRackChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // - m(MultiRackChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)), // - m(MultiRackChannelId.RACK_4_USAGE, new UnsignedWordElement(0x101B)), // - m(MultiRackChannelId.RACK_5_USAGE, new UnsignedWordElement(0x101C)) // - ), // - new FC3ReadRegistersTask(0x1017, Priority.HIGH, - m(MultiRackChannelId.START_STOP, new UnsignedWordElement(0x1017)), // - m(MultiRackChannelId.RACK_1_USAGE, new UnsignedWordElement(0x1018)), // - m(MultiRackChannelId.RACK_2_USAGE, new UnsignedWordElement(0x1019)), // - m(MultiRackChannelId.RACK_3_USAGE, new UnsignedWordElement(0x101A)), // - m(MultiRackChannelId.RACK_4_USAGE, new UnsignedWordElement(0x101B)), // - m(MultiRackChannelId.RACK_5_USAGE, new UnsignedWordElement(0x101C)) // - ), // - - new FC16WriteRegistersTask(0x101F, - m(MultiRackChannelId.SYSTEM_INSULATION_LEVEL_1, new UnsignedWordElement(0x101F)), // - m(MultiRackChannelId.SYSTEM_INSULATION_LEVEL_2, new UnsignedWordElement(0x1020)), // - new DummyRegisterElement(0x1021), // - m(MultiRackChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x1022)), // - m(MultiRackChannelId.EMS_ADDRESS, new UnsignedWordElement(0x1023)) // - ), // - - new FC3ReadRegistersTask(0x101F, Priority.LOW, - m(MultiRackChannelId.SYSTEM_INSULATION_LEVEL_1, new UnsignedWordElement(0x101F)), // - m(MultiRackChannelId.SYSTEM_INSULATION_LEVEL_2, new UnsignedWordElement(0x1020)), // - new DummyRegisterElement(0x1021), // - m(MultiRackChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x1022)), // - m(MultiRackChannelId.EMS_ADDRESS, new UnsignedWordElement(0x1023)) // - ), // - - new FC16WriteRegistersTask(ADDRESS_OFFSET_RACK_1 + 10, m(MultiRackChannelId.RACK_1_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_1 + 10)) // - ), // - new FC3ReadRegistersTask(ADDRESS_OFFSET_RACK_1 + 10, Priority.LOW, - m(MultiRackChannelId.RACK_1_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_1 + 10)) // - ), // - - new FC16WriteRegistersTask(ADDRESS_OFFSET_RACK_2 + 10, m(MultiRackChannelId.RACK_2_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_2 + 10)) // - ), // - new FC3ReadRegistersTask(ADDRESS_OFFSET_RACK_2 + 10, Priority.LOW, - m(MultiRackChannelId.RACK_2_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_2 + 10)) // - ), // - - new FC16WriteRegistersTask(ADDRESS_OFFSET_RACK_3 + 10, m(MultiRackChannelId.RACK_2_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_3 + 10)) // - ), // - new FC3ReadRegistersTask(ADDRESS_OFFSET_RACK_3 + 10, Priority.LOW, - m(MultiRackChannelId.RACK_3_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_3 + 10)) // - ), // - - new FC16WriteRegistersTask(ADDRESS_OFFSET_RACK_4 + 10, m(MultiRackChannelId.RACK_4_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_4 + 10)) // - ), // - new FC3ReadRegistersTask(ADDRESS_OFFSET_RACK_4 + 10, Priority.LOW, - m(MultiRackChannelId.RACK_4_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_4 + 10)) // - ), // - - new FC16WriteRegistersTask(ADDRESS_OFFSET_RACK_5 + 10, m(MultiRackChannelId.RACK_5_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_5 + 10)) // - ), // - new FC3ReadRegistersTask(ADDRESS_OFFSET_RACK_5 + 10, Priority.LOW, - m(MultiRackChannelId.RACK_5_POSITIVE_CONTACTOR, - new UnsignedWordElement(ADDRESS_OFFSET_RACK_5 + 10)) // - ), // - - // -------- state registers of master -------------------------------------- - new FC3ReadRegistersTask(0x1044, Priority.LOW, // - m(MultiRackChannelId.CHARGE_INDICATION, new UnsignedWordElement(0x1044)), // - m(MultiRackChannelId.CURRENT, new UnsignedWordElement(0x1045), // - ElementToChannelConverter.SCALE_FACTOR_2), // TODO Check if scale factor is correct - new DummyRegisterElement(0x1046), // - m(Battery.ChannelId.SOC, new UnsignedWordElement(0x1047)), // - m(MultiRackChannelId.SYSTEM_RUNNING_STATE, new UnsignedWordElement(0x1048)), // - m(MultiRackChannelId.VOLTAGE, new UnsignedWordElement(0x1049), // - ElementToChannelConverter.SCALE_FACTOR_2) // TODO Check if scale factor is correct - ), // - - new FC3ReadRegistersTask(0x104A, Priority.HIGH, // - m(MultiRackChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x104A)), // - new DummyRegisterElement(0x104B, 0x104C), // - m(Battery.ChannelId.CHARGE_MAX_CURRENT, new UnsignedWordElement(0x104D), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(Battery.ChannelId.DISCHARGE_MAX_CURRENT, new UnsignedWordElement(0x104E), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // - ), // - - new FC3ReadRegistersTask(0x1081, Priority.LOW, // - bm(new UnsignedWordElement(0x1081)) // - .m(MultiRackChannelId.MASTER_ALARM_LEVEL_2_INSULATION, 4) // - .m(MultiRackChannelId.MASTER_ALARM_LEVEL_1_INSULATION, 3) // - .m(MultiRackChannelId.MASTER_ALARM_PCS_EMS_CONTROL_FAIL, 2) // - .m(MultiRackChannelId.MASTER_ALARM_PCS_EMS_COMMUNICATION_FAILURE, 1) // - .m(MultiRackChannelId.MASTER_ALARM_COMMUNICATION_ERROR_WITH_SUBMASTER, 0) // - .build(), // - bm(new UnsignedWordElement(0x1082)) // - .m(MultiRackChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_5, 4) // - .m(MultiRackChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_4, 3) // - .m(MultiRackChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_3, 2) // - .m(MultiRackChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_2, 1) // - .m(MultiRackChannelId.SUB_MASTER_COMMUNICATION_FAULT_ALARM_MASTER_1, 0) // - .build(), // - bm(new UnsignedWordElement(0x1083)) // - .m(MultiRackChannelId.RACK_1_LEVEL_2_ALARM, 5) // - .m(MultiRackChannelId.RACK_1_PCS_CONTROL_FAULT, 4) // - .m(MultiRackChannelId.RACK_1_COMMUNICATION_WITH_MASTER_ERROR, 3) // - .m(MultiRackChannelId.RACK_1_DEVICE_ERROR, 2) // - .m(MultiRackChannelId.RACK_1_CYCLE_OVER_CURRENT, 1) // - .m(MultiRackChannelId.RACK_1_VOLTAGE_DIFFERENCE, 0) // - .build(), // - bm(new UnsignedWordElement(0x1084)) // - .m(MultiRackChannelId.RACK_2_LEVEL_2_ALARM, 5) // - .m(MultiRackChannelId.RACK_2_PCS_CONTROL_FAULT, 4) // - .m(MultiRackChannelId.RACK_2_COMMUNICATION_WITH_MASTER_ERROR, 3) // - .m(MultiRackChannelId.RACK_2_DEVICE_ERROR, 2) // - .m(MultiRackChannelId.RACK_2_CYCLE_OVER_CURRENT, 1) // - .m(MultiRackChannelId.RACK_2_VOLTAGE_DIFFERENCE, 0) // - .build(), // - bm(new UnsignedWordElement(0x1085)) // - .m(MultiRackChannelId.RACK_3_LEVEL_2_ALARM, 5) // - .m(MultiRackChannelId.RACK_3_PCS_CONTROL_FAULT, 4) // - .m(MultiRackChannelId.RACK_3_COMMUNICATION_WITH_MASTER_ERROR, 3) // - .m(MultiRackChannelId.RACK_3_DEVICE_ERROR, 2) // - .m(MultiRackChannelId.RACK_3_CYCLE_OVER_CURRENT, 1) // - .m(MultiRackChannelId.RACK_3_VOLTAGE_DIFFERENCE, 0) // - .build(), // - bm(new UnsignedWordElement(0x1086)) // - .m(MultiRackChannelId.RACK_4_LEVEL_2_ALARM, 5) // - .m(MultiRackChannelId.RACK_4_PCS_CONTROL_FAULT, 4) // - .m(MultiRackChannelId.RACK_4_COMMUNICATION_WITH_MASTER_ERROR, 3) // - .m(MultiRackChannelId.RACK_4_DEVICE_ERROR, 2) // - .m(MultiRackChannelId.RACK_4_CYCLE_OVER_CURRENT, 1) // - .m(MultiRackChannelId.RACK_4_VOLTAGE_DIFFERENCE, 0) // - .build(), // - bm(new UnsignedWordElement(0x1087)) // - .m(MultiRackChannelId.RACK_5_LEVEL_2_ALARM, 5) // - .m(MultiRackChannelId.RACK_5_PCS_CONTROL_FAULT, 4) // - .m(MultiRackChannelId.RACK_5_COMMUNICATION_WITH_MASTER_ERROR, 3) // - .m(MultiRackChannelId.RACK_5_DEVICE_ERROR, 2) // - .m(MultiRackChannelId.RACK_5_CYCLE_OVER_CURRENT, 1) // - .m(MultiRackChannelId.RACK_5_VOLTAGE_DIFFERENCE, 0) // - .build() // - ) // - })); - - for (SingleRack rack : this.racks) { - tasks.addAll(rack.getTasks()); - } - - return new ModbusProtocol(this, tasks.toArray(new Task[0]));// - - } - - protected final AbstractModbusElement map(io.openems.edge.common.channel.ChannelId channelId, - AbstractModbusElement element) { - return this.m(channelId, element); - } - - protected final AbstractModbusElement map(io.openems.edge.common.channel.ChannelId channelId, - AbstractModbusElement element, ElementToChannelConverter converter) { - return this.m(channelId, element, converter); - } - - protected final BitChannelMapper map(UnsignedWordElement element) { - return this.bm(element); - } - - private static Map createRackInfo() { - Map map = new HashMap(); - map.put(1, new RackInfo(ADDRESS_OFFSET_RACK_1, MultiRackChannelId.RACK_1_USAGE, - MultiRackChannelId.RACK_1_POSITIVE_CONTACTOR)); - map.put(2, new RackInfo(ADDRESS_OFFSET_RACK_2, MultiRackChannelId.RACK_2_USAGE, - MultiRackChannelId.RACK_2_POSITIVE_CONTACTOR)); - map.put(3, new RackInfo(ADDRESS_OFFSET_RACK_3, MultiRackChannelId.RACK_3_USAGE, - MultiRackChannelId.RACK_3_POSITIVE_CONTACTOR)); - map.put(4, new RackInfo(ADDRESS_OFFSET_RACK_4, MultiRackChannelId.RACK_4_USAGE, - MultiRackChannelId.RACK_4_POSITIVE_CONTACTOR)); - map.put(5, new RackInfo(ADDRESS_OFFSET_RACK_5, MultiRackChannelId.RACK_5_USAGE, - MultiRackChannelId.RACK_5_POSITIVE_CONTACTOR)); - - return map; - } - - // Helper class to get infos about connected racks - private static class RackInfo { - int addressOffset; - MultiRackChannelId usageChannelId; - MultiRackChannelId positiveContactorChannelId; - - RackInfo(int addressOffset, MultiRackChannelId usageChannelId, MultiRackChannelId positiveContactorChannelId) { - this.addressOffset = addressOffset; - this.usageChannelId = usageChannelId; - this.positiveContactorChannelId = positiveContactorChannelId; - } - } -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/State.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/State.java deleted file mode 100644 index 8cc1c52e086..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/State.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.openems.edge.battery.soltaro.multirack; - -import io.openems.edge.common.channel.OptionsEnum; - -public enum State implements OptionsEnum { - - UNDEFINED("Undefined", -1), // - PENDING("Pending", 0), // - OFF("Off", 1), // - INIT("Initializing", 2), // - RUNNING("Running", 3), // - STOPPING("Stopping", 4), // - ERROR("Error", 5), // - ERRORDELAY("Errordelay", 6); - - private State(String name, int value) { - this.name = name; - this.value = value; - } - - private int value; - private String name; - - @Override - public int getValue() { - return this.value; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public OptionsEnum getUndefined() { - return UNDEFINED; - } - -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/package-info.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/package-info.java deleted file mode 100644 index 8e452ced560..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/multirack/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.edge.battery.soltaro.multirack; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ChargeIndication.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ChargeIndication.java similarity index 83% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ChargeIndication.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ChargeIndication.java index 093e1d5b916..28136a171f2 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ChargeIndication.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ChargeIndication.java @@ -1,6 +1,6 @@ -package io.openems.edge.battery.soltaro; +package io.openems.edge.battery.soltaro.single.versiona; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ChargeIndication implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/ClusterRunState.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ClusterRunState.java similarity index 84% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/ClusterRunState.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ClusterRunState.java index 02d4801d5b6..d32e45aa57b 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/master/ClusterRunState.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ClusterRunState.java @@ -1,6 +1,6 @@ -package io.openems.edge.battery.soltaro.master; +package io.openems.edge.battery.soltaro.single.versiona; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ClusterRunState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/Config.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/Config.java similarity index 66% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/Config.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/Config.java index d9c6858b22d..0cfebaf3deb 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/Config.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/Config.java @@ -1,14 +1,22 @@ -package io.openems.edge.battery.soltaro; +package io.openems.edge.battery.soltaro.single.versiona; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.edge.battery.soltaro.BatteryState; + @ObjectClassDefinition( // - name = "BMS FENECON Soltaro", // + name = "BMS Soltaro Single Rack Version A", // description = "Implements the Soltaro battery rack system.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "bms0"; + @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 = "Modbus-ID", description = "ID of Modbus brige.") @@ -18,7 +26,7 @@ int modbusUnitId() default 1; @AttributeDefinition(name = "Capacity [Wh]", description = "The capacity of the Battery Rack.") - int capacity() default 0; + int capacity() default 50; @AttributeDefinition(name = "Battery state", description = "Switches the battery into the given state, if default is used, battery state is set automatically") BatteryState batteryState() default BatteryState.DEFAULT; @@ -26,5 +34,5 @@ @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "BMS FENECON Soltaro [{id}]"; + String webconsole_configurationFactory_nameHint() default "BMS Soltaro Single Rack Version A [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ContactorControl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ContactorControl.java similarity index 83% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ContactorControl.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ContactorControl.java index 6c5df4fccd1..30c0cdb0410 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/ContactorControl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/ContactorControl.java @@ -1,6 +1,6 @@ -package io.openems.edge.battery.soltaro; +package io.openems.edge.battery.soltaro.single.versiona; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ContactorControl implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/SoltaroRack.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/SingleRack.java similarity index 56% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/SoltaroRack.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/SingleRack.java index 560b9371eaa..f612c46102b 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/SoltaroRack.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/SingleRack.java @@ -1,4 +1,4 @@ -package io.openems.edge.battery.soltaro; +package io.openems.edge.battery.soltaro.single.versiona; import java.time.LocalDateTime; import java.util.Optional; @@ -20,23 +20,25 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.OpenemsType; import io.openems.edge.battery.api.Battery; +import io.openems.edge.battery.soltaro.BatteryState; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC6WriteRegisterTask; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.EnumReadChannel; import io.openems.edge.common.channel.EnumWriteChannel; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.common.modbusslave.ModbusSlave; @@ -45,19 +47,19 @@ @Designate(ocd = Config.class, factory = true) @Component( // - name = "Bms.Fenecon.Soltaro", // + name = "Bms.Soltaro.SingleRack.VersionA", // immediate = true, // configurationPolicy = ConfigurationPolicy.REQUIRE, // property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // ) -public class SoltaroRack extends AbstractOpenemsModbusComponent +public class SingleRack extends AbstractOpenemsModbusComponent implements Battery, OpenemsComponent, EventHandler, ModbusSlave { // Default values for the battery ranges public static final int DISCHARGE_MIN_V = 696; public static final int CHARGE_MAX_V = 854; - public static final int DISCHARGE_MAX_A = 20; - public static final int CHARGE_MAX_A = 20; + public static final int DISCHARGE_MAX_A = 0; + public static final int CHARGE_MAX_A = 0; protected final static int SYSTEM_ON = 1; protected final static int SYSTEM_OFF = 0; @@ -65,7 +67,7 @@ public class SoltaroRack extends AbstractOpenemsModbusComponent private static final int SECURITY_INTERVAL_FOR_COMMANDS_IN_SECONDS = 3; private static final int MAX_TIME_FOR_INITIALIZATION_IN_SECONDS = 30; - private final Logger log = LoggerFactory.getLogger(SoltaroRack.class); + private final Logger log = LoggerFactory.getLogger(SingleRack.class); private String modbusBridgeId; private BatteryState batteryState; @@ -82,16 +84,16 @@ public class SoltaroRack extends AbstractOpenemsModbusComponent // sent private boolean isStopping = false; - public SoltaroRack() { + public SingleRack() { super(// OpenemsComponent.ChannelId.values(), // Battery.ChannelId.values(), // - SoltaroRack.ChannelId.values() // + SingleRack.ChannelId.values() // ); - this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(SoltaroRack.CHARGE_MAX_A); - this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE).setNextValue(SoltaroRack.CHARGE_MAX_V); - this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(SoltaroRack.DISCHARGE_MAX_A); - this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE).setNextValue(SoltaroRack.DISCHARGE_MIN_V); + this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(SingleRack.CHARGE_MAX_A); + this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE).setNextValue(SingleRack.CHARGE_MAX_V); + this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(SingleRack.DISCHARGE_MAX_A); + this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE).setNextValue(SingleRack.DISCHARGE_MIN_V); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) @@ -101,7 +103,7 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); this.modbusBridgeId = config.modbus_id(); this.batteryState = config.batteryState(); @@ -169,6 +171,8 @@ private void handleBatteryState() { case ON: startSystem(); break; + case CONFIGURE: + log.error("Not possible with version A of the Soltaro batteries!"); } } @@ -228,7 +232,7 @@ private void startSystem() { try { contactorControlChannel.setNextWriteValue(SYSTEM_ON); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { log.error("Error while trying to start system\n" + e.getMessage()); } } @@ -246,7 +250,7 @@ private void stopSystem() { try { contactorControlChannel.setNextWriteValue(SYSTEM_OFF); isStopping = true; - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { log.error("Error while trying to stop system\n" + e.getMessage()); } } @@ -959,47 +963,47 @@ public Doc doc() { protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC6WriteRegisterTask(0x2010, // - m(SoltaroRack.ChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)) // + m(SingleRack.ChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)) // ), // new FC3ReadRegistersTask(0x2010, Priority.HIGH, // - m(SoltaroRack.ChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)) // + m(SingleRack.ChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)) // ), // new FC3ReadRegistersTask(0x2042, Priority.HIGH, // m(Battery.ChannelId.CHARGE_MAX_VOLTAGE, new UnsignedWordElement(0x2042), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC3ReadRegistersTask(0x2046, Priority.HIGH, // - m(SoltaroRack.ChannelId.CELL_VOLTAGE_PROTECT, new UnsignedWordElement(0x2046)), // - m(SoltaroRack.ChannelId.CELL_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)), // + m(SingleRack.ChannelId.CELL_VOLTAGE_PROTECT, new UnsignedWordElement(0x2046)), // + m(SingleRack.ChannelId.CELL_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)), // m(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE, new UnsignedWordElement(0x2048), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC6WriteRegisterTask(0x2046, // - m(SoltaroRack.ChannelId.CELL_VOLTAGE_PROTECT, new UnsignedWordElement(0x2046)) // + m(SingleRack.ChannelId.CELL_VOLTAGE_PROTECT, new UnsignedWordElement(0x2046)) // ), // new FC6WriteRegisterTask(0x2047, // - m(SoltaroRack.ChannelId.CELL_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)) // + m(SingleRack.ChannelId.CELL_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)) // ), // new FC3ReadRegistersTask(0x2100, Priority.LOW, // m(Battery.ChannelId.VOLTAGE, new UnsignedWordElement(0x2100), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // m(Battery.ChannelId.CURRENT, new UnsignedWordElement(0x2101), // ElementToChannelConverter.SCALE_FACTOR_2), // - m(SoltaroRack.ChannelId.CLUSTER_1_CHARGE_INDICATION, new UnsignedWordElement(0x2102)), // + m(SingleRack.ChannelId.CLUSTER_1_CHARGE_INDICATION, new UnsignedWordElement(0x2102)), // m(Battery.ChannelId.SOC, new UnsignedWordElement(0x2103)), // m(Battery.ChannelId.SOH, new UnsignedWordElement(0x2104)), // - m(SoltaroRack.ChannelId.CLUSTER_1_MAX_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2105)), // + m(SingleRack.ChannelId.CLUSTER_1_MAX_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2105)), // m(Battery.ChannelId.MAX_CELL_VOLTAGE, new UnsignedWordElement(0x2106)), // - m(SoltaroRack.ChannelId.CLUSTER_1_MIN_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2107)), // + m(SingleRack.ChannelId.CLUSTER_1_MIN_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2107)), // m(Battery.ChannelId.MIN_CELL_VOLTAGE, new UnsignedWordElement(0x2108)), // - m(SoltaroRack.ChannelId.CLUSTER_1_MAX_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x2109)), // + m(SingleRack.ChannelId.CLUSTER_1_MAX_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x2109)), // m(Battery.ChannelId.MAX_CELL_TEMPERATURE, new UnsignedWordElement(0x210A), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(SoltaroRack.ChannelId.CLUSTER_1_MIN_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x210B)), // + m(SingleRack.ChannelId.CLUSTER_1_MIN_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x210B)), // m(Battery.ChannelId.MIN_CELL_TEMPERATURE, new UnsignedWordElement(0x210C), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // new DummyRegisterElement(0x210D, 0x2115), // - m(SoltaroRack.ChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x2116)) // + m(SingleRack.ChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x2116)) // ), // new FC3ReadRegistersTask(0x2160, Priority.HIGH, // m(Battery.ChannelId.CHARGE_MAX_CURRENT, new UnsignedWordElement(0x2160), // @@ -1008,356 +1012,356 @@ protected ModbusProtocol defineModbusProtocol() { ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC3ReadRegistersTask(0x2140, Priority.LOW, // - bm(new UnsignedWordElement(0x2140)) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_HIGH, 0) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH, 1) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_CHA_CURRENT_HIGH, 2) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_LOW, 3) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW, 4) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_DISCHA_CURRENT_HIGH, 5) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH, 6) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_LOW, 7) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_INSULATION_LOW, 12) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH, 14) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - bm(new UnsignedWordElement(0x2141)) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_HIGH, 0) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH, 1) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CHA_CURRENT_HIGH, 2) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_LOW, 3) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW, 4) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_DISCHA_CURRENT_HIGH, 5) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH, 6) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_LOW, 7) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_SOC_LOW, 8) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH, 9) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH, 11) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_INSULATION_LOW, 12) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH, 13) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH, 14) // - .m(SoltaroRack.ChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - m(SoltaroRack.ChannelId.CLUSTER_RUN_STATE, new UnsignedWordElement(0x2142)) // + m(new BitsWordElement(0x2140, this) // + .bit(0, SingleRack.ChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_HIGH) // + .bit(1, SingleRack.ChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH) // + .bit(2, SingleRack.ChannelId.ALARM_LEVEL_2_CHA_CURRENT_HIGH) // + .bit(3, SingleRack.ChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_LOW) // + .bit(4, SingleRack.ChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW) // + .bit(5, SingleRack.ChannelId.ALARM_LEVEL_2_DISCHA_CURRENT_HIGH) // + .bit(6, SingleRack.ChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH) // + .bit(7, SingleRack.ChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_LOW) // + .bit(12,SingleRack.ChannelId.ALARM_LEVEL_2_INSULATION_LOW) // + .bit(14, SingleRack.ChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH) // + .bit(15, SingleRack.ChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW) // + ), // + m(new BitsWordElement(0x214, this) // + .bit(0, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_HIGH) // + .bit(1, SingleRack.ChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH) // + .bit(2, SingleRack.ChannelId.ALARM_LEVEL_1_CHA_CURRENT_HIGH) // + .bit(3, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_LOW) // + .bit(4, SingleRack.ChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW) // + .bit(5, SingleRack.ChannelId.ALARM_LEVEL_1_DISCHA_CURRENT_HIGH) // + .bit(6, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH) // + .bit(7, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_LOW) // + .bit(8, SingleRack.ChannelId.ALARM_LEVEL_1_SOC_LOW) // + .bit(9, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH) // + .bit(11, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH) // + .bit(12, SingleRack.ChannelId.ALARM_LEVEL_1_INSULATION_LOW) // + .bit(13, SingleRack.ChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH) // + .bit(14, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH) // + .bit(15, SingleRack.ChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW) // + ), // + m(SingleRack.ChannelId.CLUSTER_RUN_STATE, new UnsignedWordElement(0x2142)) // ), // new FC3ReadRegistersTask(0x2185, Priority.LOW, // - bm(new UnsignedWordElement(0x2185)) // - .m(SoltaroRack.ChannelId.FAILURE_SAMPLING_WIRE, 0)// - .m(SoltaroRack.ChannelId.FAILURE_CONNECTOR_WIRE, 1)// - .m(SoltaroRack.ChannelId.FAILURE_LTC6803, 2)// - .m(SoltaroRack.ChannelId.FAILURE_VOLTAGE_SAMPLING, 3)// - .m(SoltaroRack.ChannelId.FAILURE_TEMP_SAMPLING, 4)// - .m(SoltaroRack.ChannelId.FAILURE_TEMP_SENSOR, 5)// - .m(SoltaroRack.ChannelId.FAILURE_BALANCING_MODULE, 8)// - .m(SoltaroRack.ChannelId.FAILURE_TEMP_SAMPLING_LINE, 9)// - .m(SoltaroRack.ChannelId.FAILURE_INTRANET_COMMUNICATION, 10)// - .m(SoltaroRack.ChannelId.FAILURE_EEPROM, 11)// - .m(SoltaroRack.ChannelId.FAILURE_INITIALIZATION, 12)// - .build() // + m(new BitsWordElement(0x2185, this) // + .bit(0, SingleRack.ChannelId.FAILURE_SAMPLING_WIRE)// + .bit(1, SingleRack.ChannelId.FAILURE_CONNECTOR_WIRE)// + .bit(2, SingleRack.ChannelId.FAILURE_LTC6803)// + .bit(3, SingleRack.ChannelId.FAILURE_VOLTAGE_SAMPLING)// + .bit(4, SingleRack.ChannelId.FAILURE_TEMP_SAMPLING)// + .bit(5, SingleRack.ChannelId.FAILURE_TEMP_SENSOR)// + .bit(8, SingleRack.ChannelId.FAILURE_BALANCING_MODULE)// + .bit(9, SingleRack.ChannelId.FAILURE_TEMP_SAMPLING_LINE)// + .bit(10, SingleRack.ChannelId.FAILURE_INTRANET_COMMUNICATION)// + .bit(11, SingleRack.ChannelId.FAILURE_EEPROM)// + .bit(12, SingleRack.ChannelId.FAILURE_INITIALIZATION)// + ) // ), // new FC3ReadRegistersTask(0x2800, Priority.LOW, // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_000_VOLTAGE, new UnsignedWordElement(0x2800)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_001_VOLTAGE, new UnsignedWordElement(0x2801)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_002_VOLTAGE, new UnsignedWordElement(0x2802)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_003_VOLTAGE, new UnsignedWordElement(0x2803)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_004_VOLTAGE, new UnsignedWordElement(0x2804)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_005_VOLTAGE, new UnsignedWordElement(0x2805)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_006_VOLTAGE, new UnsignedWordElement(0x2806)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_007_VOLTAGE, new UnsignedWordElement(0x2807)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_008_VOLTAGE, new UnsignedWordElement(0x2808)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_009_VOLTAGE, new UnsignedWordElement(0x2809)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_010_VOLTAGE, new UnsignedWordElement(0x280A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_011_VOLTAGE, new UnsignedWordElement(0x280B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_012_VOLTAGE, new UnsignedWordElement(0x280C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_013_VOLTAGE, new UnsignedWordElement(0x280D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_014_VOLTAGE, new UnsignedWordElement(0x280E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_015_VOLTAGE, new UnsignedWordElement(0x280F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_016_VOLTAGE, new UnsignedWordElement(0x2810)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_017_VOLTAGE, new UnsignedWordElement(0x2811)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_018_VOLTAGE, new UnsignedWordElement(0x2812)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_019_VOLTAGE, new UnsignedWordElement(0x2813)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_020_VOLTAGE, new UnsignedWordElement(0x2814)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_021_VOLTAGE, new UnsignedWordElement(0x2815)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_022_VOLTAGE, new UnsignedWordElement(0x2816)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_023_VOLTAGE, new UnsignedWordElement(0x2817)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_024_VOLTAGE, new UnsignedWordElement(0x2818)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_025_VOLTAGE, new UnsignedWordElement(0x2819)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_026_VOLTAGE, new UnsignedWordElement(0x281A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_027_VOLTAGE, new UnsignedWordElement(0x281B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_028_VOLTAGE, new UnsignedWordElement(0x281C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_029_VOLTAGE, new UnsignedWordElement(0x281D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_030_VOLTAGE, new UnsignedWordElement(0x281E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_031_VOLTAGE, new UnsignedWordElement(0x281F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_032_VOLTAGE, new UnsignedWordElement(0x2820)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_033_VOLTAGE, new UnsignedWordElement(0x2821)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_034_VOLTAGE, new UnsignedWordElement(0x2822)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_035_VOLTAGE, new UnsignedWordElement(0x2823)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_036_VOLTAGE, new UnsignedWordElement(0x2824)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_037_VOLTAGE, new UnsignedWordElement(0x2825)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_038_VOLTAGE, new UnsignedWordElement(0x2826)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_039_VOLTAGE, new UnsignedWordElement(0x2827)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_040_VOLTAGE, new UnsignedWordElement(0x2828)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_041_VOLTAGE, new UnsignedWordElement(0x2829)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_042_VOLTAGE, new UnsignedWordElement(0x282A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_043_VOLTAGE, new UnsignedWordElement(0x282B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_044_VOLTAGE, new UnsignedWordElement(0x282C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_045_VOLTAGE, new UnsignedWordElement(0x282D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_046_VOLTAGE, new UnsignedWordElement(0x282E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_047_VOLTAGE, new UnsignedWordElement(0x282F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_048_VOLTAGE, new UnsignedWordElement(0x2830)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_049_VOLTAGE, new UnsignedWordElement(0x2831)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_050_VOLTAGE, new UnsignedWordElement(0x2832)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_051_VOLTAGE, new UnsignedWordElement(0x2833)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_052_VOLTAGE, new UnsignedWordElement(0x2834)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_053_VOLTAGE, new UnsignedWordElement(0x2835)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_054_VOLTAGE, new UnsignedWordElement(0x2836)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_055_VOLTAGE, new UnsignedWordElement(0x2837)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_056_VOLTAGE, new UnsignedWordElement(0x2838)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_057_VOLTAGE, new UnsignedWordElement(0x2839)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_058_VOLTAGE, new UnsignedWordElement(0x283A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_059_VOLTAGE, new UnsignedWordElement(0x283B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_060_VOLTAGE, new UnsignedWordElement(0x283C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_061_VOLTAGE, new UnsignedWordElement(0x283D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_062_VOLTAGE, new UnsignedWordElement(0x283E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_063_VOLTAGE, new UnsignedWordElement(0x283F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_064_VOLTAGE, new UnsignedWordElement(0x2840)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_065_VOLTAGE, new UnsignedWordElement(0x2841)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_066_VOLTAGE, new UnsignedWordElement(0x2842)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_067_VOLTAGE, new UnsignedWordElement(0x2843)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_068_VOLTAGE, new UnsignedWordElement(0x2844)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_069_VOLTAGE, new UnsignedWordElement(0x2845)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_070_VOLTAGE, new UnsignedWordElement(0x2846)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_071_VOLTAGE, new UnsignedWordElement(0x2847)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_072_VOLTAGE, new UnsignedWordElement(0x2848)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_073_VOLTAGE, new UnsignedWordElement(0x2849)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_074_VOLTAGE, new UnsignedWordElement(0x284A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_075_VOLTAGE, new UnsignedWordElement(0x284B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_076_VOLTAGE, new UnsignedWordElement(0x284C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_077_VOLTAGE, new UnsignedWordElement(0x284D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_078_VOLTAGE, new UnsignedWordElement(0x284E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_079_VOLTAGE, new UnsignedWordElement(0x284F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_080_VOLTAGE, new UnsignedWordElement(0x2850)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_081_VOLTAGE, new UnsignedWordElement(0x2851)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_082_VOLTAGE, new UnsignedWordElement(0x2852)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_083_VOLTAGE, new UnsignedWordElement(0x2853)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_084_VOLTAGE, new UnsignedWordElement(0x2854)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_085_VOLTAGE, new UnsignedWordElement(0x2855)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_086_VOLTAGE, new UnsignedWordElement(0x2856)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_087_VOLTAGE, new UnsignedWordElement(0x2857)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_088_VOLTAGE, new UnsignedWordElement(0x2858)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_089_VOLTAGE, new UnsignedWordElement(0x2859)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_090_VOLTAGE, new UnsignedWordElement(0x285A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_091_VOLTAGE, new UnsignedWordElement(0x285B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_092_VOLTAGE, new UnsignedWordElement(0x285C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_093_VOLTAGE, new UnsignedWordElement(0x285D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_094_VOLTAGE, new UnsignedWordElement(0x285E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_095_VOLTAGE, new UnsignedWordElement(0x285F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_096_VOLTAGE, new UnsignedWordElement(0x2860)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_097_VOLTAGE, new UnsignedWordElement(0x2861)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_098_VOLTAGE, new UnsignedWordElement(0x2862)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_099_VOLTAGE, new UnsignedWordElement(0x2863)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_100_VOLTAGE, new UnsignedWordElement(0x2864)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_101_VOLTAGE, new UnsignedWordElement(0x2865)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_102_VOLTAGE, new UnsignedWordElement(0x2866)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_103_VOLTAGE, new UnsignedWordElement(0x2867)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_104_VOLTAGE, new UnsignedWordElement(0x2868)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_105_VOLTAGE, new UnsignedWordElement(0x2869)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_106_VOLTAGE, new UnsignedWordElement(0x286A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_107_VOLTAGE, new UnsignedWordElement(0x286B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_108_VOLTAGE, new UnsignedWordElement(0x286C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_109_VOLTAGE, new UnsignedWordElement(0x286D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_110_VOLTAGE, new UnsignedWordElement(0x286E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_111_VOLTAGE, new UnsignedWordElement(0x286F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_112_VOLTAGE, new UnsignedWordElement(0x2870)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_113_VOLTAGE, new UnsignedWordElement(0x2871)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_114_VOLTAGE, new UnsignedWordElement(0x2872)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_115_VOLTAGE, new UnsignedWordElement(0x2873)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_116_VOLTAGE, new UnsignedWordElement(0x2874)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_117_VOLTAGE, new UnsignedWordElement(0x2875)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_118_VOLTAGE, new UnsignedWordElement(0x2876)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_119_VOLTAGE, new UnsignedWordElement(0x2877)) // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_000_VOLTAGE, new UnsignedWordElement(0x2800)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_001_VOLTAGE, new UnsignedWordElement(0x2801)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_002_VOLTAGE, new UnsignedWordElement(0x2802)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_003_VOLTAGE, new UnsignedWordElement(0x2803)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_004_VOLTAGE, new UnsignedWordElement(0x2804)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_005_VOLTAGE, new UnsignedWordElement(0x2805)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_006_VOLTAGE, new UnsignedWordElement(0x2806)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_007_VOLTAGE, new UnsignedWordElement(0x2807)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_008_VOLTAGE, new UnsignedWordElement(0x2808)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_009_VOLTAGE, new UnsignedWordElement(0x2809)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_010_VOLTAGE, new UnsignedWordElement(0x280A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_011_VOLTAGE, new UnsignedWordElement(0x280B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_012_VOLTAGE, new UnsignedWordElement(0x280C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_013_VOLTAGE, new UnsignedWordElement(0x280D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_014_VOLTAGE, new UnsignedWordElement(0x280E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_015_VOLTAGE, new UnsignedWordElement(0x280F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_016_VOLTAGE, new UnsignedWordElement(0x2810)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_017_VOLTAGE, new UnsignedWordElement(0x2811)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_018_VOLTAGE, new UnsignedWordElement(0x2812)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_019_VOLTAGE, new UnsignedWordElement(0x2813)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_020_VOLTAGE, new UnsignedWordElement(0x2814)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_021_VOLTAGE, new UnsignedWordElement(0x2815)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_022_VOLTAGE, new UnsignedWordElement(0x2816)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_023_VOLTAGE, new UnsignedWordElement(0x2817)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_024_VOLTAGE, new UnsignedWordElement(0x2818)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_025_VOLTAGE, new UnsignedWordElement(0x2819)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_026_VOLTAGE, new UnsignedWordElement(0x281A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_027_VOLTAGE, new UnsignedWordElement(0x281B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_028_VOLTAGE, new UnsignedWordElement(0x281C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_029_VOLTAGE, new UnsignedWordElement(0x281D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_030_VOLTAGE, new UnsignedWordElement(0x281E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_031_VOLTAGE, new UnsignedWordElement(0x281F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_032_VOLTAGE, new UnsignedWordElement(0x2820)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_033_VOLTAGE, new UnsignedWordElement(0x2821)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_034_VOLTAGE, new UnsignedWordElement(0x2822)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_035_VOLTAGE, new UnsignedWordElement(0x2823)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_036_VOLTAGE, new UnsignedWordElement(0x2824)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_037_VOLTAGE, new UnsignedWordElement(0x2825)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_038_VOLTAGE, new UnsignedWordElement(0x2826)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_039_VOLTAGE, new UnsignedWordElement(0x2827)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_040_VOLTAGE, new UnsignedWordElement(0x2828)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_041_VOLTAGE, new UnsignedWordElement(0x2829)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_042_VOLTAGE, new UnsignedWordElement(0x282A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_043_VOLTAGE, new UnsignedWordElement(0x282B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_044_VOLTAGE, new UnsignedWordElement(0x282C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_045_VOLTAGE, new UnsignedWordElement(0x282D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_046_VOLTAGE, new UnsignedWordElement(0x282E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_047_VOLTAGE, new UnsignedWordElement(0x282F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_048_VOLTAGE, new UnsignedWordElement(0x2830)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_049_VOLTAGE, new UnsignedWordElement(0x2831)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_050_VOLTAGE, new UnsignedWordElement(0x2832)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_051_VOLTAGE, new UnsignedWordElement(0x2833)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_052_VOLTAGE, new UnsignedWordElement(0x2834)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_053_VOLTAGE, new UnsignedWordElement(0x2835)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_054_VOLTAGE, new UnsignedWordElement(0x2836)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_055_VOLTAGE, new UnsignedWordElement(0x2837)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_056_VOLTAGE, new UnsignedWordElement(0x2838)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_057_VOLTAGE, new UnsignedWordElement(0x2839)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_058_VOLTAGE, new UnsignedWordElement(0x283A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_059_VOLTAGE, new UnsignedWordElement(0x283B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_060_VOLTAGE, new UnsignedWordElement(0x283C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_061_VOLTAGE, new UnsignedWordElement(0x283D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_062_VOLTAGE, new UnsignedWordElement(0x283E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_063_VOLTAGE, new UnsignedWordElement(0x283F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_064_VOLTAGE, new UnsignedWordElement(0x2840)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_065_VOLTAGE, new UnsignedWordElement(0x2841)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_066_VOLTAGE, new UnsignedWordElement(0x2842)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_067_VOLTAGE, new UnsignedWordElement(0x2843)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_068_VOLTAGE, new UnsignedWordElement(0x2844)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_069_VOLTAGE, new UnsignedWordElement(0x2845)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_070_VOLTAGE, new UnsignedWordElement(0x2846)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_071_VOLTAGE, new UnsignedWordElement(0x2847)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_072_VOLTAGE, new UnsignedWordElement(0x2848)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_073_VOLTAGE, new UnsignedWordElement(0x2849)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_074_VOLTAGE, new UnsignedWordElement(0x284A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_075_VOLTAGE, new UnsignedWordElement(0x284B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_076_VOLTAGE, new UnsignedWordElement(0x284C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_077_VOLTAGE, new UnsignedWordElement(0x284D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_078_VOLTAGE, new UnsignedWordElement(0x284E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_079_VOLTAGE, new UnsignedWordElement(0x284F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_080_VOLTAGE, new UnsignedWordElement(0x2850)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_081_VOLTAGE, new UnsignedWordElement(0x2851)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_082_VOLTAGE, new UnsignedWordElement(0x2852)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_083_VOLTAGE, new UnsignedWordElement(0x2853)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_084_VOLTAGE, new UnsignedWordElement(0x2854)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_085_VOLTAGE, new UnsignedWordElement(0x2855)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_086_VOLTAGE, new UnsignedWordElement(0x2856)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_087_VOLTAGE, new UnsignedWordElement(0x2857)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_088_VOLTAGE, new UnsignedWordElement(0x2858)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_089_VOLTAGE, new UnsignedWordElement(0x2859)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_090_VOLTAGE, new UnsignedWordElement(0x285A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_091_VOLTAGE, new UnsignedWordElement(0x285B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_092_VOLTAGE, new UnsignedWordElement(0x285C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_093_VOLTAGE, new UnsignedWordElement(0x285D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_094_VOLTAGE, new UnsignedWordElement(0x285E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_095_VOLTAGE, new UnsignedWordElement(0x285F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_096_VOLTAGE, new UnsignedWordElement(0x2860)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_097_VOLTAGE, new UnsignedWordElement(0x2861)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_098_VOLTAGE, new UnsignedWordElement(0x2862)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_099_VOLTAGE, new UnsignedWordElement(0x2863)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_100_VOLTAGE, new UnsignedWordElement(0x2864)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_101_VOLTAGE, new UnsignedWordElement(0x2865)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_102_VOLTAGE, new UnsignedWordElement(0x2866)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_103_VOLTAGE, new UnsignedWordElement(0x2867)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_104_VOLTAGE, new UnsignedWordElement(0x2868)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_105_VOLTAGE, new UnsignedWordElement(0x2869)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_106_VOLTAGE, new UnsignedWordElement(0x286A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_107_VOLTAGE, new UnsignedWordElement(0x286B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_108_VOLTAGE, new UnsignedWordElement(0x286C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_109_VOLTAGE, new UnsignedWordElement(0x286D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_110_VOLTAGE, new UnsignedWordElement(0x286E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_111_VOLTAGE, new UnsignedWordElement(0x286F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_112_VOLTAGE, new UnsignedWordElement(0x2870)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_113_VOLTAGE, new UnsignedWordElement(0x2871)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_114_VOLTAGE, new UnsignedWordElement(0x2872)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_115_VOLTAGE, new UnsignedWordElement(0x2873)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_116_VOLTAGE, new UnsignedWordElement(0x2874)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_117_VOLTAGE, new UnsignedWordElement(0x2875)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_118_VOLTAGE, new UnsignedWordElement(0x2876)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_119_VOLTAGE, new UnsignedWordElement(0x2877)) // ), // new FC3ReadRegistersTask(0x2878, Priority.LOW, // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_120_VOLTAGE, new UnsignedWordElement(0x2878)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_121_VOLTAGE, new UnsignedWordElement(0x2879)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_122_VOLTAGE, new UnsignedWordElement(0x287A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_123_VOLTAGE, new UnsignedWordElement(0x287B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_124_VOLTAGE, new UnsignedWordElement(0x287C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_125_VOLTAGE, new UnsignedWordElement(0x287D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_126_VOLTAGE, new UnsignedWordElement(0x287E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_127_VOLTAGE, new UnsignedWordElement(0x287F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_128_VOLTAGE, new UnsignedWordElement(0x2880)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_129_VOLTAGE, new UnsignedWordElement(0x2881)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_130_VOLTAGE, new UnsignedWordElement(0x2882)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_131_VOLTAGE, new UnsignedWordElement(0x2883)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_132_VOLTAGE, new UnsignedWordElement(0x2884)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_133_VOLTAGE, new UnsignedWordElement(0x2885)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_134_VOLTAGE, new UnsignedWordElement(0x2886)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_135_VOLTAGE, new UnsignedWordElement(0x2887)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_136_VOLTAGE, new UnsignedWordElement(0x2888)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_137_VOLTAGE, new UnsignedWordElement(0x2889)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_138_VOLTAGE, new UnsignedWordElement(0x288A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_139_VOLTAGE, new UnsignedWordElement(0x288B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_140_VOLTAGE, new UnsignedWordElement(0x288C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_141_VOLTAGE, new UnsignedWordElement(0x288D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_142_VOLTAGE, new UnsignedWordElement(0x288E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_143_VOLTAGE, new UnsignedWordElement(0x288F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_144_VOLTAGE, new UnsignedWordElement(0x2890)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_145_VOLTAGE, new UnsignedWordElement(0x2891)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_146_VOLTAGE, new UnsignedWordElement(0x2892)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_147_VOLTAGE, new UnsignedWordElement(0x2893)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_148_VOLTAGE, new UnsignedWordElement(0x2894)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_149_VOLTAGE, new UnsignedWordElement(0x2895)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_150_VOLTAGE, new UnsignedWordElement(0x2896)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_151_VOLTAGE, new UnsignedWordElement(0x2897)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_152_VOLTAGE, new UnsignedWordElement(0x2898)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_153_VOLTAGE, new UnsignedWordElement(0x2899)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_154_VOLTAGE, new UnsignedWordElement(0x289A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_155_VOLTAGE, new UnsignedWordElement(0x289B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_156_VOLTAGE, new UnsignedWordElement(0x289C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_157_VOLTAGE, new UnsignedWordElement(0x289D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_158_VOLTAGE, new UnsignedWordElement(0x289E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_159_VOLTAGE, new UnsignedWordElement(0x289F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_160_VOLTAGE, new UnsignedWordElement(0x28A0)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_161_VOLTAGE, new UnsignedWordElement(0x28A1)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_162_VOLTAGE, new UnsignedWordElement(0x28A2)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_163_VOLTAGE, new UnsignedWordElement(0x28A3)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_164_VOLTAGE, new UnsignedWordElement(0x28A4)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_165_VOLTAGE, new UnsignedWordElement(0x28A5)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_166_VOLTAGE, new UnsignedWordElement(0x28A6)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_167_VOLTAGE, new UnsignedWordElement(0x28A7)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_168_VOLTAGE, new UnsignedWordElement(0x28A8)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_169_VOLTAGE, new UnsignedWordElement(0x28A9)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_170_VOLTAGE, new UnsignedWordElement(0x28AA)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_171_VOLTAGE, new UnsignedWordElement(0x28AB)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_172_VOLTAGE, new UnsignedWordElement(0x28AC)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_173_VOLTAGE, new UnsignedWordElement(0x28AD)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_174_VOLTAGE, new UnsignedWordElement(0x28AE)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_175_VOLTAGE, new UnsignedWordElement(0x28AF)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_176_VOLTAGE, new UnsignedWordElement(0x28B0)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_177_VOLTAGE, new UnsignedWordElement(0x28B1)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_178_VOLTAGE, new UnsignedWordElement(0x28B2)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_179_VOLTAGE, new UnsignedWordElement(0x28B3)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_180_VOLTAGE, new UnsignedWordElement(0x28B4)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_181_VOLTAGE, new UnsignedWordElement(0x28B5)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_182_VOLTAGE, new UnsignedWordElement(0x28B6)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_183_VOLTAGE, new UnsignedWordElement(0x28B7)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_184_VOLTAGE, new UnsignedWordElement(0x28B8)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_185_VOLTAGE, new UnsignedWordElement(0x28B9)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_186_VOLTAGE, new UnsignedWordElement(0x28BA)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_187_VOLTAGE, new UnsignedWordElement(0x28BB)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_188_VOLTAGE, new UnsignedWordElement(0x28BC)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_189_VOLTAGE, new UnsignedWordElement(0x28BD)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_190_VOLTAGE, new UnsignedWordElement(0x28BE)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_191_VOLTAGE, new UnsignedWordElement(0x28BF)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_192_VOLTAGE, new UnsignedWordElement(0x28C0)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_193_VOLTAGE, new UnsignedWordElement(0x28C1)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_194_VOLTAGE, new UnsignedWordElement(0x28C2)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_195_VOLTAGE, new UnsignedWordElement(0x28C3)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_196_VOLTAGE, new UnsignedWordElement(0x28C4)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_197_VOLTAGE, new UnsignedWordElement(0x28C5)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_198_VOLTAGE, new UnsignedWordElement(0x28C6)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_199_VOLTAGE, new UnsignedWordElement(0x28C7)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_200_VOLTAGE, new UnsignedWordElement(0x28C8)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_201_VOLTAGE, new UnsignedWordElement(0x28C9)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_202_VOLTAGE, new UnsignedWordElement(0x28CA)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_203_VOLTAGE, new UnsignedWordElement(0x28CB)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_204_VOLTAGE, new UnsignedWordElement(0x28CC)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_205_VOLTAGE, new UnsignedWordElement(0x28CD)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_206_VOLTAGE, new UnsignedWordElement(0x28CE)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_207_VOLTAGE, new UnsignedWordElement(0x28CF)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_208_VOLTAGE, new UnsignedWordElement(0x28D0)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_209_VOLTAGE, new UnsignedWordElement(0x28D1)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_210_VOLTAGE, new UnsignedWordElement(0x28D2)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_211_VOLTAGE, new UnsignedWordElement(0x28D3)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_212_VOLTAGE, new UnsignedWordElement(0x28D4)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_213_VOLTAGE, new UnsignedWordElement(0x28D5)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_214_VOLTAGE, new UnsignedWordElement(0x28D6)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_215_VOLTAGE, new UnsignedWordElement(0x28D7)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_216_VOLTAGE, new UnsignedWordElement(0x28D8)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_217_VOLTAGE, new UnsignedWordElement(0x28D9)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_218_VOLTAGE, new UnsignedWordElement(0x28DA)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_219_VOLTAGE, new UnsignedWordElement(0x28DB)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_220_VOLTAGE, new UnsignedWordElement(0x28DC)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_221_VOLTAGE, new UnsignedWordElement(0x28DD)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_222_VOLTAGE, new UnsignedWordElement(0x28DE)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_223_VOLTAGE, new UnsignedWordElement(0x28DF)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_224_VOLTAGE, new UnsignedWordElement(0x28E0)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_225_VOLTAGE, new UnsignedWordElement(0x28E1)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_226_VOLTAGE, new UnsignedWordElement(0x28E2)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_227_VOLTAGE, new UnsignedWordElement(0x28E3)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_228_VOLTAGE, new UnsignedWordElement(0x28E4)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_229_VOLTAGE, new UnsignedWordElement(0x28E5)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_230_VOLTAGE, new UnsignedWordElement(0x28E6)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_231_VOLTAGE, new UnsignedWordElement(0x28E7)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_232_VOLTAGE, new UnsignedWordElement(0x28E8)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_233_VOLTAGE, new UnsignedWordElement(0x28E9)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_234_VOLTAGE, new UnsignedWordElement(0x28EA)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_235_VOLTAGE, new UnsignedWordElement(0x28EB)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_236_VOLTAGE, new UnsignedWordElement(0x28EC)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_237_VOLTAGE, new UnsignedWordElement(0x28ED)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_238_VOLTAGE, new UnsignedWordElement(0x28EE)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_239_VOLTAGE, new UnsignedWordElement(0x28EF)) // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_120_VOLTAGE, new UnsignedWordElement(0x2878)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_121_VOLTAGE, new UnsignedWordElement(0x2879)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_122_VOLTAGE, new UnsignedWordElement(0x287A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_123_VOLTAGE, new UnsignedWordElement(0x287B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_124_VOLTAGE, new UnsignedWordElement(0x287C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_125_VOLTAGE, new UnsignedWordElement(0x287D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_126_VOLTAGE, new UnsignedWordElement(0x287E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_127_VOLTAGE, new UnsignedWordElement(0x287F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_128_VOLTAGE, new UnsignedWordElement(0x2880)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_129_VOLTAGE, new UnsignedWordElement(0x2881)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_130_VOLTAGE, new UnsignedWordElement(0x2882)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_131_VOLTAGE, new UnsignedWordElement(0x2883)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_132_VOLTAGE, new UnsignedWordElement(0x2884)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_133_VOLTAGE, new UnsignedWordElement(0x2885)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_134_VOLTAGE, new UnsignedWordElement(0x2886)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_135_VOLTAGE, new UnsignedWordElement(0x2887)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_136_VOLTAGE, new UnsignedWordElement(0x2888)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_137_VOLTAGE, new UnsignedWordElement(0x2889)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_138_VOLTAGE, new UnsignedWordElement(0x288A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_139_VOLTAGE, new UnsignedWordElement(0x288B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_140_VOLTAGE, new UnsignedWordElement(0x288C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_141_VOLTAGE, new UnsignedWordElement(0x288D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_142_VOLTAGE, new UnsignedWordElement(0x288E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_143_VOLTAGE, new UnsignedWordElement(0x288F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_144_VOLTAGE, new UnsignedWordElement(0x2890)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_145_VOLTAGE, new UnsignedWordElement(0x2891)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_146_VOLTAGE, new UnsignedWordElement(0x2892)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_147_VOLTAGE, new UnsignedWordElement(0x2893)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_148_VOLTAGE, new UnsignedWordElement(0x2894)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_149_VOLTAGE, new UnsignedWordElement(0x2895)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_150_VOLTAGE, new UnsignedWordElement(0x2896)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_151_VOLTAGE, new UnsignedWordElement(0x2897)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_152_VOLTAGE, new UnsignedWordElement(0x2898)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_153_VOLTAGE, new UnsignedWordElement(0x2899)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_154_VOLTAGE, new UnsignedWordElement(0x289A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_155_VOLTAGE, new UnsignedWordElement(0x289B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_156_VOLTAGE, new UnsignedWordElement(0x289C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_157_VOLTAGE, new UnsignedWordElement(0x289D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_158_VOLTAGE, new UnsignedWordElement(0x289E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_159_VOLTAGE, new UnsignedWordElement(0x289F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_160_VOLTAGE, new UnsignedWordElement(0x28A0)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_161_VOLTAGE, new UnsignedWordElement(0x28A1)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_162_VOLTAGE, new UnsignedWordElement(0x28A2)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_163_VOLTAGE, new UnsignedWordElement(0x28A3)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_164_VOLTAGE, new UnsignedWordElement(0x28A4)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_165_VOLTAGE, new UnsignedWordElement(0x28A5)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_166_VOLTAGE, new UnsignedWordElement(0x28A6)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_167_VOLTAGE, new UnsignedWordElement(0x28A7)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_168_VOLTAGE, new UnsignedWordElement(0x28A8)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_169_VOLTAGE, new UnsignedWordElement(0x28A9)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_170_VOLTAGE, new UnsignedWordElement(0x28AA)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_171_VOLTAGE, new UnsignedWordElement(0x28AB)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_172_VOLTAGE, new UnsignedWordElement(0x28AC)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_173_VOLTAGE, new UnsignedWordElement(0x28AD)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_174_VOLTAGE, new UnsignedWordElement(0x28AE)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_175_VOLTAGE, new UnsignedWordElement(0x28AF)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_176_VOLTAGE, new UnsignedWordElement(0x28B0)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_177_VOLTAGE, new UnsignedWordElement(0x28B1)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_178_VOLTAGE, new UnsignedWordElement(0x28B2)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_179_VOLTAGE, new UnsignedWordElement(0x28B3)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_180_VOLTAGE, new UnsignedWordElement(0x28B4)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_181_VOLTAGE, new UnsignedWordElement(0x28B5)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_182_VOLTAGE, new UnsignedWordElement(0x28B6)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_183_VOLTAGE, new UnsignedWordElement(0x28B7)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_184_VOLTAGE, new UnsignedWordElement(0x28B8)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_185_VOLTAGE, new UnsignedWordElement(0x28B9)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_186_VOLTAGE, new UnsignedWordElement(0x28BA)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_187_VOLTAGE, new UnsignedWordElement(0x28BB)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_188_VOLTAGE, new UnsignedWordElement(0x28BC)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_189_VOLTAGE, new UnsignedWordElement(0x28BD)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_190_VOLTAGE, new UnsignedWordElement(0x28BE)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_191_VOLTAGE, new UnsignedWordElement(0x28BF)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_192_VOLTAGE, new UnsignedWordElement(0x28C0)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_193_VOLTAGE, new UnsignedWordElement(0x28C1)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_194_VOLTAGE, new UnsignedWordElement(0x28C2)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_195_VOLTAGE, new UnsignedWordElement(0x28C3)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_196_VOLTAGE, new UnsignedWordElement(0x28C4)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_197_VOLTAGE, new UnsignedWordElement(0x28C5)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_198_VOLTAGE, new UnsignedWordElement(0x28C6)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_199_VOLTAGE, new UnsignedWordElement(0x28C7)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_200_VOLTAGE, new UnsignedWordElement(0x28C8)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_201_VOLTAGE, new UnsignedWordElement(0x28C9)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_202_VOLTAGE, new UnsignedWordElement(0x28CA)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_203_VOLTAGE, new UnsignedWordElement(0x28CB)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_204_VOLTAGE, new UnsignedWordElement(0x28CC)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_205_VOLTAGE, new UnsignedWordElement(0x28CD)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_206_VOLTAGE, new UnsignedWordElement(0x28CE)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_207_VOLTAGE, new UnsignedWordElement(0x28CF)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_208_VOLTAGE, new UnsignedWordElement(0x28D0)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_209_VOLTAGE, new UnsignedWordElement(0x28D1)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_210_VOLTAGE, new UnsignedWordElement(0x28D2)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_211_VOLTAGE, new UnsignedWordElement(0x28D3)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_212_VOLTAGE, new UnsignedWordElement(0x28D4)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_213_VOLTAGE, new UnsignedWordElement(0x28D5)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_214_VOLTAGE, new UnsignedWordElement(0x28D6)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_215_VOLTAGE, new UnsignedWordElement(0x28D7)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_216_VOLTAGE, new UnsignedWordElement(0x28D8)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_217_VOLTAGE, new UnsignedWordElement(0x28D9)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_218_VOLTAGE, new UnsignedWordElement(0x28DA)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_219_VOLTAGE, new UnsignedWordElement(0x28DB)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_220_VOLTAGE, new UnsignedWordElement(0x28DC)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_221_VOLTAGE, new UnsignedWordElement(0x28DD)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_222_VOLTAGE, new UnsignedWordElement(0x28DE)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_223_VOLTAGE, new UnsignedWordElement(0x28DF)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_224_VOLTAGE, new UnsignedWordElement(0x28E0)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_225_VOLTAGE, new UnsignedWordElement(0x28E1)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_226_VOLTAGE, new UnsignedWordElement(0x28E2)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_227_VOLTAGE, new UnsignedWordElement(0x28E3)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_228_VOLTAGE, new UnsignedWordElement(0x28E4)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_229_VOLTAGE, new UnsignedWordElement(0x28E5)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_230_VOLTAGE, new UnsignedWordElement(0x28E6)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_231_VOLTAGE, new UnsignedWordElement(0x28E7)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_232_VOLTAGE, new UnsignedWordElement(0x28E8)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_233_VOLTAGE, new UnsignedWordElement(0x28E9)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_234_VOLTAGE, new UnsignedWordElement(0x28EA)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_235_VOLTAGE, new UnsignedWordElement(0x28EB)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_236_VOLTAGE, new UnsignedWordElement(0x28EC)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_237_VOLTAGE, new UnsignedWordElement(0x28ED)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_238_VOLTAGE, new UnsignedWordElement(0x28EE)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_239_VOLTAGE, new UnsignedWordElement(0x28EF)) // ), // new FC3ReadRegistersTask(0x2C00, Priority.LOW, // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_00_TEMPERATURE, new UnsignedWordElement(0x2C00)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_01_TEMPERATURE, new UnsignedWordElement(0x2C01)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_02_TEMPERATURE, new UnsignedWordElement(0x2C02)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_03_TEMPERATURE, new UnsignedWordElement(0x2C03)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_04_TEMPERATURE, new UnsignedWordElement(0x2C04)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_05_TEMPERATURE, new UnsignedWordElement(0x2C05)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_06_TEMPERATURE, new UnsignedWordElement(0x2C06)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_07_TEMPERATURE, new UnsignedWordElement(0x2C07)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_08_TEMPERATURE, new UnsignedWordElement(0x2C08)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_09_TEMPERATURE, new UnsignedWordElement(0x2C09)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_10_TEMPERATURE, new UnsignedWordElement(0x2C0A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_11_TEMPERATURE, new UnsignedWordElement(0x2C0B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_12_TEMPERATURE, new UnsignedWordElement(0x2C0C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_13_TEMPERATURE, new UnsignedWordElement(0x2C0D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_14_TEMPERATURE, new UnsignedWordElement(0x2C0E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_15_TEMPERATURE, new UnsignedWordElement(0x2C0F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_16_TEMPERATURE, new UnsignedWordElement(0x2C10)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_17_TEMPERATURE, new UnsignedWordElement(0x2C11)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_18_TEMPERATURE, new UnsignedWordElement(0x2C12)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_19_TEMPERATURE, new UnsignedWordElement(0x2C13)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_20_TEMPERATURE, new UnsignedWordElement(0x2C14)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_21_TEMPERATURE, new UnsignedWordElement(0x2C15)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_22_TEMPERATURE, new UnsignedWordElement(0x2C16)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_23_TEMPERATURE, new UnsignedWordElement(0x2C17)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_24_TEMPERATURE, new UnsignedWordElement(0x2C18)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_25_TEMPERATURE, new UnsignedWordElement(0x2C19)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_26_TEMPERATURE, new UnsignedWordElement(0x2C1A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_27_TEMPERATURE, new UnsignedWordElement(0x2C1B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_28_TEMPERATURE, new UnsignedWordElement(0x2C1C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_29_TEMPERATURE, new UnsignedWordElement(0x2C1D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_30_TEMPERATURE, new UnsignedWordElement(0x2C1E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_31_TEMPERATURE, new UnsignedWordElement(0x2C1F)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_32_TEMPERATURE, new UnsignedWordElement(0x2C20)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_33_TEMPERATURE, new UnsignedWordElement(0x2C21)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_34_TEMPERATURE, new UnsignedWordElement(0x2C22)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_35_TEMPERATURE, new UnsignedWordElement(0x2C23)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_36_TEMPERATURE, new UnsignedWordElement(0x2C24)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_37_TEMPERATURE, new UnsignedWordElement(0x2C25)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_38_TEMPERATURE, new UnsignedWordElement(0x2C26)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_39_TEMPERATURE, new UnsignedWordElement(0x2C27)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_40_TEMPERATURE, new UnsignedWordElement(0x2C28)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_41_TEMPERATURE, new UnsignedWordElement(0x2C29)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_42_TEMPERATURE, new UnsignedWordElement(0x2C2A)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_43_TEMPERATURE, new UnsignedWordElement(0x2C2B)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_44_TEMPERATURE, new UnsignedWordElement(0x2C2C)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_45_TEMPERATURE, new UnsignedWordElement(0x2C2D)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_46_TEMPERATURE, new UnsignedWordElement(0x2C2E)), // - m(SoltaroRack.ChannelId.CLUSTER_1_BATTERY_47_TEMPERATURE, new UnsignedWordElement(0x2C2F)) // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_00_TEMPERATURE, new UnsignedWordElement(0x2C00)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_01_TEMPERATURE, new UnsignedWordElement(0x2C01)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_02_TEMPERATURE, new UnsignedWordElement(0x2C02)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_03_TEMPERATURE, new UnsignedWordElement(0x2C03)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_04_TEMPERATURE, new UnsignedWordElement(0x2C04)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_05_TEMPERATURE, new UnsignedWordElement(0x2C05)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_06_TEMPERATURE, new UnsignedWordElement(0x2C06)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_07_TEMPERATURE, new UnsignedWordElement(0x2C07)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_08_TEMPERATURE, new UnsignedWordElement(0x2C08)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_09_TEMPERATURE, new UnsignedWordElement(0x2C09)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_10_TEMPERATURE, new UnsignedWordElement(0x2C0A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_11_TEMPERATURE, new UnsignedWordElement(0x2C0B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_12_TEMPERATURE, new UnsignedWordElement(0x2C0C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_13_TEMPERATURE, new UnsignedWordElement(0x2C0D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_14_TEMPERATURE, new UnsignedWordElement(0x2C0E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_15_TEMPERATURE, new UnsignedWordElement(0x2C0F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_16_TEMPERATURE, new UnsignedWordElement(0x2C10)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_17_TEMPERATURE, new UnsignedWordElement(0x2C11)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_18_TEMPERATURE, new UnsignedWordElement(0x2C12)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_19_TEMPERATURE, new UnsignedWordElement(0x2C13)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_20_TEMPERATURE, new UnsignedWordElement(0x2C14)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_21_TEMPERATURE, new UnsignedWordElement(0x2C15)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_22_TEMPERATURE, new UnsignedWordElement(0x2C16)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_23_TEMPERATURE, new UnsignedWordElement(0x2C17)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_24_TEMPERATURE, new UnsignedWordElement(0x2C18)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_25_TEMPERATURE, new UnsignedWordElement(0x2C19)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_26_TEMPERATURE, new UnsignedWordElement(0x2C1A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_27_TEMPERATURE, new UnsignedWordElement(0x2C1B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_28_TEMPERATURE, new UnsignedWordElement(0x2C1C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_29_TEMPERATURE, new UnsignedWordElement(0x2C1D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_30_TEMPERATURE, new UnsignedWordElement(0x2C1E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_31_TEMPERATURE, new UnsignedWordElement(0x2C1F)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_32_TEMPERATURE, new UnsignedWordElement(0x2C20)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_33_TEMPERATURE, new UnsignedWordElement(0x2C21)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_34_TEMPERATURE, new UnsignedWordElement(0x2C22)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_35_TEMPERATURE, new UnsignedWordElement(0x2C23)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_36_TEMPERATURE, new UnsignedWordElement(0x2C24)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_37_TEMPERATURE, new UnsignedWordElement(0x2C25)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_38_TEMPERATURE, new UnsignedWordElement(0x2C26)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_39_TEMPERATURE, new UnsignedWordElement(0x2C27)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_40_TEMPERATURE, new UnsignedWordElement(0x2C28)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_41_TEMPERATURE, new UnsignedWordElement(0x2C29)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_42_TEMPERATURE, new UnsignedWordElement(0x2C2A)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_43_TEMPERATURE, new UnsignedWordElement(0x2C2B)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_44_TEMPERATURE, new UnsignedWordElement(0x2C2C)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_45_TEMPERATURE, new UnsignedWordElement(0x2C2D)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_46_TEMPERATURE, new UnsignedWordElement(0x2C2E)), // + m(SingleRack.ChannelId.CLUSTER_1_BATTERY_47_TEMPERATURE, new UnsignedWordElement(0x2C2F)) // )// ); // } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(), // - Battery.getModbusSlaveNatureTable()); + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + Battery.getModbusSlaveNatureTable(accessMode)); } } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/Config.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/Config.java similarity index 67% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/Config.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/Config.java index 27474bb1223..67b5c7075c9 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/Config.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/Config.java @@ -1,54 +1,62 @@ -package io.openems.edge.battery.soltaro.versionb; +package io.openems.edge.battery.soltaro.single.versionb; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.edge.battery.soltaro.BatteryState; @ObjectClassDefinition( // - name = "BMS FENECON Soltaro Version B", // + name = "BMS Soltaro Single Rack Version B", // description = "Implements the Soltaro battery rack system.") @interface Config { - String service_pid(); + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "bms0"; + @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 = "Modbus-ID", description = "ID of Modbus brige.") - String modbus_id(); - + String modbus_id() default "modbus0"; + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") - int modbusUnitId() default 1; - + int modbusUnitId() default 0; + @AttributeDefinition(name = "Battery state", description = "Switches the battery into the given state, if default is used, battery state is set automatically") BatteryState batteryState() default BatteryState.DEFAULT; - - @AttributeDefinition(name = "Number of slaves", description = "The number of slaves in this battery rack (max. 22)", min = "1", max = "22") + + @AttributeDefinition(name = "Number of slaves", description = "The number of slaves in this battery rack (max. 20)", min = "1", max = "20") int numberOfSlaves() default 20; @AttributeDefinition(name = "Error Level 2 Delay", description = "Sets the delay time in seconds how long the system should be stopped after an error level 2 has occurred") int errorLevel2Delay() default 600; - + @AttributeDefinition(name = "Max Start Attempts", description = "Sets the counter how many time the system should try to start") int maxStartAppempts() default 5; - + @AttributeDefinition(name = "Max Start Time", description = "Max Time in seconds allowed for starting the system") - int maxStartTime() default 20; - + int maxStartTime() default 30; + @AttributeDefinition(name = "Start Not Successful Delay Time", description = "Sets the delay time in seconds how long the system should be stopped if it was not able to start") int startUnsuccessfulDelay() default 3600; - + @AttributeDefinition(name = "Watchdog", description = "Watchdog timeout in seconds") int watchdog() default 60; - - @AttributeDefinition(name = "SoC Low Alarm", description = "Sets the value for BMS SoC protection (0..100)", min="0", max="100") + + @AttributeDefinition(name = "SoC Low Alarm", description = "Sets the value for BMS SoC protection (0..100)", min = "0", max = "100") int SoCLowAlarm() default 0; - + + @AttributeDefinition(name = "Minimal Cell Voltage Millivolt", description = "Minimal cell voltage in milli volt when system does not allow further discharging") + int minimalCellVoltage() default 2800; + @AttributeDefinition(name = "Reduce tasks", description = "Reduces read and write tasks to avoid errors") boolean ReduceTasks() default false; - + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - - String webconsole_configurationFactory_nameHint() default "BMS FENECON Soltaro [{id}]"; + + String webconsole_configurationFactory_nameHint() default "BMS Soltaro Single Rack Version B [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/VersionBEnums.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/Enums.java similarity index 97% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/VersionBEnums.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/Enums.java index 04b7ecc20b9..621e32a295e 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/VersionBEnums.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/Enums.java @@ -1,8 +1,8 @@ -package io.openems.edge.battery.soltaro.versionb; +package io.openems.edge.battery.soltaro.single.versionb; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; -public class VersionBEnums { +public class Enums { public enum FanStatus implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/SingleRack.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/SingleRack.java new file mode 100644 index 00000000000..42629ff154a --- /dev/null +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/SingleRack.java @@ -0,0 +1,1336 @@ +package io.openems.edge.battery.soltaro.single.versionb; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +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.event.Event; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.metatype.annotations.Designate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.channel.Unit; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.battery.api.Battery; +import io.openems.edge.battery.soltaro.single.versionb.Enums.AutoSetFunction; +import io.openems.edge.battery.soltaro.single.versionb.Enums.ContactorControl; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; +import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; +import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; +import io.openems.edge.bridge.modbus.api.element.SignedWordElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; +import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; +import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; +import io.openems.edge.bridge.modbus.api.task.FC6WriteRegisterTask; +import io.openems.edge.bridge.modbus.api.task.Task; +import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.channel.EnumReadChannel; +import io.openems.edge.common.channel.EnumWriteChannel; +import io.openems.edge.common.channel.IntegerDoc; +import io.openems.edge.common.channel.IntegerReadChannel; +import io.openems.edge.common.channel.IntegerWriteChannel; +import io.openems.edge.common.channel.StateChannel; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.battery.soltaro.ModuleParameters; +import io.openems.edge.battery.soltaro.State; +import io.openems.edge.battery.soltaro.ChannelIdImpl; + +@Designate(ocd = Config.class, factory = true) +@Component( // + name = "Bms.Soltaro.SingleRack.VersionB", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE, // + property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // +) +public class SingleRack extends AbstractOpenemsModbusComponent + implements Battery, OpenemsComponent, EventHandler { // , JsonApi TODO + + protected static final int SYSTEM_ON = 1; + protected final static int SYSTEM_OFF = 0; + + private static final String KEY_TEMPERATURE = "_TEMPERATURE"; + private static final String KEY_VOLTAGE = "_VOLTAGE"; + private static final Integer SYSTEM_RESET = 0x1; + private static final String NUMBER_FORMAT = "%03d"; // creates string number with leading zeros + + @Reference + protected ConfigurationAdmin cm; + + private final Logger log = LoggerFactory.getLogger(SingleRack.class); + private String modbusBridgeId; + private State state = State.UNDEFINED; + // if configuring is needed this is used to go through the necessary steps + private ConfiguringProcess nextConfiguringProcess = ConfiguringProcess.NONE; + private Config config; + private Map> channelMap; + // If an error has occurred, this indicates the time when next action could be + // done + private LocalDateTime errorDelayIsOver = null; + private int unsuccessfulStarts = 0; + private LocalDateTime startAttemptTime = null; + + private LocalDateTime timeAfterAutoId = null; + private LocalDateTime configuringFinished = null; + private int DELAY_AUTO_ID_SECONDS = 5; + private int DELAY_AFTER_CONFIGURING_FINISHED = 5; + + public SingleRack() { + super(// + OpenemsComponent.ChannelId.values(), // + Battery.ChannelId.values(), // + SingleRackChannelId.values() // + ); + } + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + @Activate + void activate(ComponentContext context, Config config) { + this.config = config; + + // adds dynamically created channels and save them into a map to access them + // when modbus tasks are created + channelMap = createDynamicChannels(); + + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + config.modbus_id()); + this.modbusBridgeId = config.modbus_id(); + initializeCallbacks(); + + setWatchdog(config.watchdog()); + setSoCLowAlarm(config.SoCLowAlarm()); + setCapacity(); + } + + private void setCapacity() { + int capacity = this.config.numberOfSlaves() * ModuleParameters.CAPACITY_WH.getValue() / 1000; + this.channel(Battery.ChannelId.CAPACITY).setNextValue(capacity); + } + + private void handleStateMachine() { + log.info("SingleRack.handleStateMachine(): State: " + this.getStateMachineState()); + boolean readyForWorking = false; + switch (this.getStateMachineState()) { + case ERROR: + stopSystem(); + errorDelayIsOver = LocalDateTime.now().plusSeconds(config.errorLevel2Delay()); + setStateMachineState(State.ERRORDELAY); + break; + + case ERRORDELAY: + if (LocalDateTime.now().isAfter(errorDelayIsOver)) { + errorDelayIsOver = null; + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else { + this.setStateMachineState(State.OFF); + } + } + break; + case INIT: + if (this.isSystemRunning()) { + this.setStateMachineState(State.RUNNING); + unsuccessfulStarts = 0; + startAttemptTime = null; + } else { + if (startAttemptTime.plusSeconds(config.maxStartTime()).isBefore(LocalDateTime.now())) { + startAttemptTime = null; + unsuccessfulStarts++; + this.stopSystem(); + this.setStateMachineState(State.STOPPING); + if (unsuccessfulStarts >= config.maxStartAppempts()) { + errorDelayIsOver = LocalDateTime.now().plusSeconds(config.startUnsuccessfulDelay()); + this.setStateMachineState(State.ERRORDELAY); + unsuccessfulStarts = 0; + } + } + } + break; + case OFF: + log.debug("in case 'OFF'; try to start the system"); + this.startSystem(); + log.debug("set state to 'INIT'"); + this.setStateMachineState(State.INIT); + startAttemptTime = LocalDateTime.now(); + break; + case RUNNING: + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else if (!this.isSystemRunning()) { + this.setStateMachineState(State.UNDEFINED); + } else { + // if minimal cell voltage is lower than configured minimal cell voltage, then force system to charge + IntegerReadChannel minCellVoltageChannel = this.channel(SingleRackChannelId.CLUSTER_1_MIN_CELL_VOLTAGE); + Optional minCellVoltageOpt = minCellVoltageChannel.value().asOptional(); + if (minCellVoltageOpt.isPresent()) { + int minCellVoltage = minCellVoltageOpt.get(); + if (minCellVoltage < this.config.minimalCellVoltage()) { + // set the discharge current negative to force the system to charge + // TODO check if this is working! + this.getDischargeMaxCurrent().setNextValue( (-1) * this.getChargeMaxCurrent().value().get() ); + } + } + readyForWorking = true; + } + break; + case STOPPING: + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else { + if (this.isSystemStopped()) { + this.setStateMachineState(State.OFF); + } + } + break; + case UNDEFINED: + if (this.isError()) { + this.setStateMachineState(State.ERROR); + } else if (this.isSystemStopped()) { + this.setStateMachineState(State.OFF); + } else if (this.isSystemRunning()) { + this.setStateMachineState(State.RUNNING); + } else if (this.isSystemStatePending()) { + this.setStateMachineState(State.PENDING); + } + break; + case PENDING: + this.stopSystem(); + this.setStateMachineState(State.OFF); + break; + } + + this.getReadyForWorking().setNextValue(readyForWorking); + } + + /* + * creates a map containing channels for voltage and temperature depending on + * the number of modules + */ + private Map> createDynamicChannels() { + Map> map = new HashMap<>(); + + int voltSensors = ModuleParameters.VOLTAGE_SENSORS_PER_MODULE.getValue(); + for (int i = 0; i < this.config.numberOfSlaves(); i++) { + for (int j = i * voltSensors; j < (i + 1) * voltSensors; j++) { + String key = getSingleCellPrefix(j) + KEY_VOLTAGE; + IntegerDoc doc = new IntegerDoc(); + io.openems.edge.common.channel.ChannelId channelId = new ChannelIdImpl(key, doc.unit(Unit.MILLIVOLT)); + IntegerReadChannel integerReadChannel = (IntegerReadChannel) this.addChannel(channelId); + map.put(key, integerReadChannel); + } + } + + int tempSensors = ModuleParameters.TEMPERATURE_SENSORS_PER_MODULE.getValue(); + for (int i = 0; i < this.config.numberOfSlaves(); i++) { + for (int j = i * tempSensors; j < (i + 1) * tempSensors; j++) { + String key = getSingleCellPrefix(j) + KEY_TEMPERATURE; + + IntegerDoc doc = new IntegerDoc(); + io.openems.edge.common.channel.ChannelId channelId = new ChannelIdImpl(key, + doc.unit(Unit.DEZIDEGREE_CELSIUS)); + IntegerReadChannel integerReadChannel = (IntegerReadChannel) this.addChannel(channelId); + map.put(key, integerReadChannel); + } + } + return map; + } + + private String getSingleCellPrefix(int num) { + return "CLUSTER_1_BATTERY_" + String.format(NUMBER_FORMAT, num); + } + + private void setWatchdog(int time_seconds) { + try { + IntegerWriteChannel c = this.channel(SingleRackChannelId.EMS_COMMUNICATION_TIMEOUT); + c.setNextWriteValue(time_seconds); + } catch (OpenemsNamedException e) { + log.error("Error while setting ems timeout!\n" + e.getMessage()); + } + } + + @Deactivate + protected void deactivate() { + // Remove dynamically created channels when component is deactivated + for (Channel c : this.channelMap.values()) { + this.removeChannel(c); + } + super.deactivate(); + } + + private void initializeCallbacks() { + + this.channel(SingleRackChannelId.CLUSTER_1_VOLTAGE).onChange(value -> { + @SuppressWarnings("unchecked") + Optional vOpt = (Optional) value.asOptional(); + if (!vOpt.isPresent()) { + return; + } + int voltage_volt = (int) (vOpt.get() * 0.001); + log.debug("callback voltage, value: " + voltage_volt); + this.channel(Battery.ChannelId.VOLTAGE).setNextValue(voltage_volt); + }); + + this.channel(SingleRackChannelId.CLUSTER_1_MIN_CELL_VOLTAGE).onChange(value -> { + @SuppressWarnings("unchecked") + Optional vOpt = (Optional) value.asOptional(); + if (!vOpt.isPresent()) { + return; + } + int voltage_millivolt = vOpt.get(); + log.debug("callback min cell voltage, value: " + voltage_millivolt); + this.channel(Battery.ChannelId.MIN_CELL_VOLTAGE).setNextValue(voltage_millivolt); + }); + + // write battery ranges to according channels in battery api + // MAX_VOLTAGE x2082 + this.channel(SingleRackChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM).onChange(value -> { + @SuppressWarnings("unchecked") + Optional vOpt = (Optional) value.asOptional(); + if (!vOpt.isPresent()) { + return; + } + int max_charge_voltage = (int) (vOpt.get() * 0.001); + log.debug("callback battery range, max charge voltage, value: " + max_charge_voltage); + this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE).setNextValue(max_charge_voltage); + }); + + // DISCHARGE_MIN_VOLTAGE 0x2088 + this.channel(SingleRackChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM).onChange(value -> { + @SuppressWarnings("unchecked") + Optional vOpt = (Optional) value.asOptional(); + if (!vOpt.isPresent()) { + return; + } + int min_discharge_voltage = (int) (vOpt.get() * 0.001); + log.debug("callback battery range, min discharge voltage, value: " + min_discharge_voltage); + this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE).setNextValue(min_discharge_voltage); + }); + + // CHARGE_MAX_CURRENT 0x2160 + this.channel(SingleRackChannelId.SYSTEM_MAX_CHARGE_CURRENT).onChange(value -> { + @SuppressWarnings("unchecked") + Optional cOpt = (Optional) value.asOptional(); + if (!cOpt.isPresent()) { + return; + } + int max_current = (int) (cOpt.get() * 0.001); + log.debug("callback battery range, max charge current, value: " + max_current); + this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(max_current); + }); + + // DISCHARGE_MAX_CURRENT 0x2161 + this.channel(SingleRackChannelId.SYSTEM_MAX_DISCHARGE_CURRENT).onChange(value -> { + @SuppressWarnings("unchecked") + Optional cOpt = (Optional) value.asOptional(); + if (!cOpt.isPresent()) { + return; + } + int max_current = (int) (cOpt.get() * 0.001); + log.debug("callback battery range, max discharge current, value: " + max_current); + this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(max_current); + }); + + } + + @Override + public void handleEvent(Event event) { + if (!this.isEnabled()) { + return; + } + switch (event.getTopic()) { + + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: + handleBatteryState(); + break; + } + } + + private void handleBatteryState() { + switch (config.batteryState()) { + case DEFAULT: + handleStateMachine(); + break; + case OFF: + stopSystem(); + break; + case ON: + startSystem(); + break; + case CONFIGURE: + configureSlaves(); + break; + + } + } + + private void configureSlaves() { + if (nextConfiguringProcess == ConfiguringProcess.NONE) { + nextConfiguringProcess = ConfiguringProcess.CONFIGURING_STARTED; + } + + switch (nextConfiguringProcess) { + case CONFIGURING_STARTED: + System.out.println(" ===> CONFIGURING STARTED: setNumberOfModules() <==="); + setNumberOfModules(); + break; + case SET_ID_AUTO_CONFIGURING: + System.out.println(" ===> SET_ID_AUTO_CONFIGURING: setIdAutoConfiguring() <==="); + setIdAutoConfiguring(); + break; + case CHECK_ID_AUTO_CONFIGURING: + if (timeAfterAutoId != null) { + if (timeAfterAutoId.plusSeconds(DELAY_AUTO_ID_SECONDS).isAfter(LocalDateTime.now())) { + break; + } else { + timeAfterAutoId = null; + } + } + System.out.println(" ===> CHECK_ID_AUTO_CONFIGURING: checkIdAutoConfiguring() <==="); + checkIdAutoConfiguring(); + break; + case SET_TEMPERATURE_ID_AUTO_CONFIGURING: + System.out.println(" ===> SET_TEMPERATURE_ID_AUTO_CONFIGURING: setTemperatureIdAutoConfiguring() <==="); + setTemperatureIdAutoConfiguring(); + break; + case CHECK_TEMPERATURE_ID_AUTO_CONFIGURING: + if (timeAfterAutoId != null) { + if (timeAfterAutoId.plusSeconds(DELAY_AUTO_ID_SECONDS).isAfter(LocalDateTime.now())) { + break; + } else { + timeAfterAutoId = null; + } + } + System.out.println(" ===> CHECK_TEMPERATURE_ID_AUTO_CONFIGURING: checkTemperatureIdAutoConfiguring() <==="); + checkTemperatureIdAutoConfiguring(); + break; + case SET_VOLTAGE_RANGES: + System.out.println(" ===> SET_VOLTAGE_RANGES: setVoltageRanges() <==="); + setVoltageRanges(); + + break; + case CONFIGURING_FINISHED: + System.out.println("====>>> Configuring successful! <<<===="); + + if (configuringFinished == null) { + nextConfiguringProcess = ConfiguringProcess.RESTART_AFTER_SETTING; + } else { + if (configuringFinished.plusSeconds(DELAY_AFTER_CONFIGURING_FINISHED).isAfter(LocalDateTime.now())) { + System.out.println(">>> Delay time after configuring!"); + } else { + System.out.println("Delay time after configuring is over, reset system"); + IntegerWriteChannel resetChannel = this.channel(SingleRackChannelId.SYSTEM_RESET); + try { + resetChannel.setNextWriteValue(SYSTEM_RESET); + configuringFinished = null; + } catch (OpenemsNamedException e) { + System.out.println("Error while trying to reset the system!"); + } + } + } + break; + case RESTART_AFTER_SETTING: + // A manual restart is needed + System.out.println("====>>> Please restart system manually!"); + case NONE: + break; + } + } + + private void setVoltageRanges() { + + try { + IntegerWriteChannel level1OverVoltageChannel = this + .channel(SingleRackChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM); + level1OverVoltageChannel.setNextWriteValue( + this.config.numberOfSlaves() * ModuleParameters.LEVEL_1_TOTAL_OVER_VOLTAGE_MILLIVOLT.getValue()); + + IntegerWriteChannel level1OverVoltageChannelRecover = this + .channel(SingleRackChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER); + level1OverVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() + * ModuleParameters.LEVEL_1_TOTAL_OVER_VOLTAGE_RECOVER_MILLIVOLT.getValue()); + + IntegerWriteChannel level1LowVoltageChannel = this + .channel(SingleRackChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM); + level1LowVoltageChannel.setNextWriteValue( + this.config.numberOfSlaves() * ModuleParameters.LEVEL_1_TOTAL_LOW_VOLTAGE_MILLIVOLT.getValue()); + + IntegerWriteChannel level1LowVoltageChannelRecover = this + .channel(SingleRackChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER); + level1LowVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() + * ModuleParameters.LEVEL_1_TOTAL_LOW_VOLTAGE_RECOVER_MILLIVOLT.getValue()); + + IntegerWriteChannel level2OverVoltageChannel = this + .channel(SingleRackChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_PROTECTION); + level2OverVoltageChannel.setNextWriteValue( + this.config.numberOfSlaves() * ModuleParameters.LEVEL_2_TOTAL_OVER_VOLTAGE_MILLIVOLT.getValue()); + + IntegerWriteChannel level2OverVoltageChannelRecover = this + .channel(SingleRackChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER); + level2OverVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() + * ModuleParameters.LEVEL_2_TOTAL_OVER_VOLTAGE_RECOVER_MILLIVOLT.getValue()); + + IntegerWriteChannel level2LowVoltageChannel = this + .channel(SingleRackChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_PROTECTION); + level2LowVoltageChannel.setNextWriteValue( + this.config.numberOfSlaves() * ModuleParameters.LEVEL_2_TOTAL_LOW_VOLTAGE_MILLIVOLT.getValue()); + + IntegerWriteChannel level2LowVoltageChannelRecover = this + .channel(SingleRackChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER); + level2LowVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() + * ModuleParameters.LEVEL_2_TOTAL_LOW_VOLTAGE_RECOVER_MILLIVOLT.getValue()); + + nextConfiguringProcess = ConfiguringProcess.CONFIGURING_FINISHED; + configuringFinished = LocalDateTime.now(); + + } catch (OpenemsNamedException e) { + log.error("Setting voltage ranges not successful!"); + } + + } + + private void checkTemperatureIdAutoConfiguring() { + IntegerReadChannel autoSetTemperatureSlavesIdChannel = this + .channel(SingleRackChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID); + Optional autoSetTemperatureSlavesIdOpt = autoSetTemperatureSlavesIdChannel.value().asOptional(); + if (!autoSetTemperatureSlavesIdOpt.isPresent()) { + return; + } + int autoSetTemperatureSlaves = autoSetTemperatureSlavesIdOpt.get(); + if (autoSetTemperatureSlaves == Enums.AutoSetFunction.FAILURE.getValue()) { + log.error("Auto set temperature slaves id failed! Start configuring process again!"); + // Auto set failed, try again + nextConfiguringProcess = ConfiguringProcess.CONFIGURING_STARTED; + } else if (autoSetTemperatureSlaves == Enums.AutoSetFunction.SUCCES.getValue()) { + log.info("Auto set temperature slaves id succeeded!"); + nextConfiguringProcess = ConfiguringProcess.SET_VOLTAGE_RANGES; + } + } + + private void setTemperatureIdAutoConfiguring() { + + IntegerWriteChannel autoSetSlavesTemperatureIdChannel = this + .channel(SingleRackChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID); + try { + autoSetSlavesTemperatureIdChannel.setNextWriteValue(AutoSetFunction.START_AUTO_SETTING.getValue()); + timeAfterAutoId = LocalDateTime.now(); + nextConfiguringProcess = ConfiguringProcess.CHECK_TEMPERATURE_ID_AUTO_CONFIGURING; + } catch (OpenemsNamedException e) { + log.error("Setting temperature id auto set not successful"); // Set was not successful, it will be tried + // until it succeeded + } + } + + private void checkIdAutoConfiguring() { + IntegerReadChannel autoSetSlavesIdChannel = this.channel(SingleRackChannelId.AUTO_SET_SLAVES_ID); + Optional autoSetSlavesIdOpt = autoSetSlavesIdChannel.value().asOptional(); + if (!autoSetSlavesIdOpt.isPresent()) { + return; + } + int autoSetSlaves = autoSetSlavesIdOpt.get(); + if (autoSetSlaves == Enums.AutoSetFunction.FAILURE.getValue()) { + log.error("Auto set slaves id failed! Start configuring process again!"); + // Auto set failed, try again + nextConfiguringProcess = ConfiguringProcess.CONFIGURING_STARTED; + } else if (autoSetSlaves == Enums.AutoSetFunction.SUCCES.getValue()) { + log.info("Auto set slaves id succeeded!"); + nextConfiguringProcess = ConfiguringProcess.SET_TEMPERATURE_ID_AUTO_CONFIGURING; + } + } + + private void setIdAutoConfiguring() { + // Set number of modules + IntegerWriteChannel autoSetSlavesIdChannel = this.channel(SingleRackChannelId.AUTO_SET_SLAVES_ID); + try { + autoSetSlavesIdChannel.setNextWriteValue(AutoSetFunction.START_AUTO_SETTING.getValue()); + timeAfterAutoId = LocalDateTime.now(); + nextConfiguringProcess = ConfiguringProcess.CHECK_ID_AUTO_CONFIGURING; + } catch (OpenemsNamedException e) { + log.error("Setting slave numbers not successful"); // Set was not successful, it will be tried until it + // succeeded + } + } + + private void setNumberOfModules() { + // Set number of modules + IntegerWriteChannel numberOfSlavesChannel = this + .channel(SingleRackChannelId.WORK_PARAMETER_PCS_COMMUNICATION_RATE); + try { + numberOfSlavesChannel.setNextWriteValue(this.config.numberOfSlaves()); + nextConfiguringProcess = ConfiguringProcess.SET_ID_AUTO_CONFIGURING; + } catch (OpenemsNamedException e) { + log.error("Setting slave numbers not successful"); // Set was not successful, it will be tried until it + // succeeded + } + } + + private enum ConfiguringProcess { + NONE, CONFIGURING_STARTED, SET_ID_AUTO_CONFIGURING, CHECK_ID_AUTO_CONFIGURING, + SET_TEMPERATURE_ID_AUTO_CONFIGURING, CHECK_TEMPERATURE_ID_AUTO_CONFIGURING, SET_VOLTAGE_RANGES, + CONFIGURING_FINISHED, RESTART_AFTER_SETTING + } + + private boolean isSystemRunning() { + EnumReadChannel contactorControlChannel = this.channel(SingleRackChannelId.BMS_CONTACTOR_CONTROL); + ContactorControl cc = contactorControlChannel.value().asEnum(); + return cc == ContactorControl.ON_GRID; + } + + private boolean isSystemStopped() { + EnumReadChannel contactorControlChannel = this.channel(SingleRackChannelId.BMS_CONTACTOR_CONTROL); + ContactorControl cc = contactorControlChannel.value().asEnum(); + return cc == ContactorControl.CUT_OFF; + } + + /** + * Checks whether system has an undefined state, e.g. rack 1 & 2 are configured, + * but only rack 1 is running. This state can only be reached at startup coming + * from state undefined + */ + private boolean isSystemStatePending() { + return !isSystemRunning() && !isSystemStopped(); + } + + private boolean isAlarmLevel2Error() { + return (readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CHA_CURRENT_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_LOW) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_DISCHA_CURRENT_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_LOW) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_SOC_LOW) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_TEMPERATURE_DIFFERENCE_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_POLES_TEMPERATURE_DIFFERENCE_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_DIFFERENCE_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_INSULATION_LOW) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_DIFFERENCE_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH) + || readValueFromBooleanChannel(SingleRackChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)); + } + + private boolean isSlaveCommunicationError() { + boolean b = false; + switch (this.config.numberOfSlaves()) { + case 20: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_20_COMMUNICATION_ERROR); + case 19: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_19_COMMUNICATION_ERROR); + case 18: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_18_COMMUNICATION_ERROR); + case 17: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_17_COMMUNICATION_ERROR); + case 16: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_16_COMMUNICATION_ERROR); + case 15: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_15_COMMUNICATION_ERROR); + case 14: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_14_COMMUNICATION_ERROR); + case 13: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_13_COMMUNICATION_ERROR); + case 12: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_12_COMMUNICATION_ERROR); + case 11: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_11_COMMUNICATION_ERROR); + case 10: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_10_COMMUNICATION_ERROR); + case 9: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_9_COMMUNICATION_ERROR); + case 8: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_8_COMMUNICATION_ERROR); + case 7: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_7_COMMUNICATION_ERROR); + case 6: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_6_COMMUNICATION_ERROR); + case 5: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_5_COMMUNICATION_ERROR); + case 4: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_4_COMMUNICATION_ERROR); + case 3: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_3_COMMUNICATION_ERROR); + case 2: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_2_COMMUNICATION_ERROR); + case 1: + b = b || readValueFromBooleanChannel(SingleRackChannelId.SLAVE_1_COMMUNICATION_ERROR); + } + + return b; + } + + private boolean isError() { + return isAlarmLevel2Error() || isSlaveCommunicationError(); + } + + private boolean readValueFromBooleanChannel(SingleRackChannelId singleRackChannelId) { + StateChannel r = this.channel(singleRackChannelId); + Optional bOpt = r.value().asOptional(); + return bOpt.isPresent() && bOpt.get(); + } + + public String getModbusBridgeId() { + return modbusBridgeId; + } + + @Override + public String debugLog() { + return "SoC:" + this.getSoc().value() // + + "|Discharge:" + this.getDischargeMinVoltage().value() + ";" + this.getDischargeMaxCurrent().value() // + + "|Charge:" + this.getChargeMaxVoltage().value() + ";" + this.getChargeMaxCurrent().value() + "|State:" + + this.getStateMachineState(); + } + + private void startSystem() { + EnumWriteChannel contactorControlChannel = this.channel(SingleRackChannelId.BMS_CONTACTOR_CONTROL); + ContactorControl cc = contactorControlChannel.value().asEnum(); + // To avoid hardware damages do not send start command if system has already + // started + if (cc == ContactorControl.ON_GRID || cc == ContactorControl.CONNECTION_INITIATING) { + return; + } + + try { + log.debug("write value to contactor control channel: value: " + SYSTEM_ON); + contactorControlChannel.setNextWriteValue(SYSTEM_ON); + } catch (OpenemsNamedException e) { + log.error("Error while trying to start system\n" + e.getMessage()); + } + } + + private void stopSystem() { + EnumWriteChannel contactorControlChannel = this.channel(SingleRackChannelId.BMS_CONTACTOR_CONTROL); + ContactorControl cc = contactorControlChannel.value().asEnum(); + // To avoid hardware damages do not send stop command if system has already + // stopped + if (cc == ContactorControl.CUT_OFF) { + return; + } + + try { + log.debug("write value to contactor control channel: value: " + SYSTEM_OFF); + contactorControlChannel.setNextWriteValue(SYSTEM_OFF); + } catch (OpenemsNamedException e) { + log.error("Error while trying to stop system\n" + e.getMessage()); + } + } + + public State getStateMachineState() { + return state; + } + + public void setStateMachineState(State state) { + this.state = state; + this.channel(SingleRackChannelId.STATE_MACHINE).setNextValue(this.state); + } + + private void setSoCLowAlarm(int soCLowAlarm) { + try { + ((IntegerWriteChannel) this.channel(SingleRackChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION)) + .setNextWriteValue(soCLowAlarm); + ((IntegerWriteChannel) this.channel(SingleRackChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION_RECOVER)) + .setNextWriteValue(soCLowAlarm); + } catch (OpenemsNamedException e) { + log.error("Error while setting parameter for soc low protection!" + e.getMessage()); + } + } + + @Override + protected ModbusProtocol defineModbusProtocol() { + + ModbusProtocol protocol = new ModbusProtocol(this, // + // Main switch + new FC6WriteRegisterTask(0x2010, + m(SingleRackChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)) // + ), + + // System reset + new FC6WriteRegisterTask(0x2004, m(SingleRackChannelId.SYSTEM_RESET, new UnsignedWordElement(0x2004)) // + ), + + // EMS timeout --> Watchdog + new FC6WriteRegisterTask(0x201C, + m(SingleRackChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x201C)) // + ), + // Sleep + new FC6WriteRegisterTask(0x201D, m(SingleRackChannelId.SLEEP, new UnsignedWordElement(0x201D)) // + ), + + // Work parameter + new FC6WriteRegisterTask(0x20C1, + m(SingleRackChannelId.WORK_PARAMETER_PCS_COMMUNICATION_RATE, new UnsignedWordElement(0x20C1)) // + ), + + // Paramaeters for configuring + new FC6WriteRegisterTask(0x2014, + m(SingleRackChannelId.AUTO_SET_SLAVES_ID, new UnsignedWordElement(0x2014))), + new FC6WriteRegisterTask(0x2019, + m(SingleRackChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID, new UnsignedWordElement(0x2019))), + + // Control registers + new FC3ReadRegistersTask(0x2000, Priority.HIGH, // + m(SingleRackChannelId.FAN_STATUS, new UnsignedWordElement(0x2000)), // + m(SingleRackChannelId.MAIN_CONTACTOR_STATE, new UnsignedWordElement(0x2001)), // + m(SingleRackChannelId.DRY_CONTACT_1_EXPORT, new UnsignedWordElement(0x2002)), // + m(SingleRackChannelId.DRY_CONTACT_2_EXPORT, new UnsignedWordElement(0x2003)), // + m(SingleRackChannelId.SYSTEM_RESET, new UnsignedWordElement(0x2004)), // + m(SingleRackChannelId.SYSTEM_RUN_MODE, new UnsignedWordElement(0x2005)), // + m(SingleRackChannelId.PRE_CONTACTOR_STATUS, new UnsignedWordElement(0x2006)), // + m(new BitsWordElement(0x2007, this) // + .bit(15, SingleRackChannelId.ALARM_FLAG_STATUS_DISCHARGE_TEMPERATURE_LOW) // + .bit(14, SingleRackChannelId.ALARM_FLAG_STATUS_DISCHARGE_TEMPERATURE_HIGH) // + .bit(13, SingleRackChannelId.ALARM_FLAG_STATUS_VOLTAGE_DIFFERENCE) // + .bit(12, SingleRackChannelId.ALARM_FLAG_STATUS_INSULATION_LOW) // + .bit(11, SingleRackChannelId.ALARM_FLAG_STATUS_CELL_VOLTAGE_DIFFERENCE) // + .bit(10, SingleRackChannelId.ALARM_FLAG_STATUS_ELECTRODE_TEMPERATURE_HIGH) // + .bit(9, SingleRackChannelId.ALARM_FLAG_STATUS_TEMPERATURE_DIFFERENCE) // + .bit(8, SingleRackChannelId.ALARM_FLAG_STATUS_SOC_LOW) // + .bit(7, SingleRackChannelId.ALARM_FLAG_STATUS_CELL_OVER_TEMPERATURE) // + .bit(6, SingleRackChannelId.ALARM_FLAG_STATUS_CELL_LOW_TEMPERATURE) // + .bit(5, SingleRackChannelId.ALARM_FLAG_STATUS_DISCHARGE_OVER_CURRENT) // + .bit(4, SingleRackChannelId.ALARM_FLAG_STATUS_SYSTEM_LOW_VOLTAGE) // + .bit(3, SingleRackChannelId.ALARM_FLAG_STATUS_CELL_LOW_VOLTAGE) // + .bit(2, SingleRackChannelId.ALARM_FLAG_STATUS_CHARGE_OVER_CURRENT) // + .bit(1, SingleRackChannelId.ALARM_FLAG_STATUS_SYSTEM_OVER_VOLTAGE) // + .bit(0, SingleRackChannelId.ALARM_FLAG_STATUS_CELL_OVER_VOLTAGE) // + ), // + m(new BitsWordElement(0x2008, this) // + .bit(15, SingleRackChannelId.PROTECT_FLAG_STATUS_DISCHARGE_TEMPERATURE_LOW) // + .bit(14, SingleRackChannelId.PROTECT_FLAG_STATUS_DISCHARGE_TEMPERATURE_HIGH) // + .bit(13, SingleRackChannelId.PROTECT_FLAG_STATUS_VOLTAGE_DIFFERENCE) // + .bit(12, SingleRackChannelId.PROTECT_FLAG_STATUS_INSULATION_LOW) // + .bit(11, SingleRackChannelId.PROTECT_FLAG_STATUS_CELL_VOLTAGE_DIFFERENCE) // + .bit(10, SingleRackChannelId.PROTECT_FLAG_STATUS_ELECTRODE_TEMPERATURE_HIGH) // + .bit(9, SingleRackChannelId.PROTECT_FLAG_STATUS_TEMPERATURE_DIFFERENCE) // + .bit(8, SingleRackChannelId.PROTECT_FLAG_STATUS_SOC_LOW) // + .bit(7, SingleRackChannelId.PROTECT_FLAG_STATUS_CELL_OVER_TEMPERATURE) // + .bit(6, SingleRackChannelId.PROTECT_FLAG_STATUS_CELL_LOW_TEMPERATURE) // + .bit(5, SingleRackChannelId.PROTECT_FLAG_STATUS_DISCHARGE_OVER_CURRENT) // + .bit(4, SingleRackChannelId.PROTECT_FLAG_STATUS_SYSTEM_LOW_VOLTAGE) // + .bit(3, SingleRackChannelId.PROTECT_FLAG_STATUS_CELL_LOW_VOLTAGE) // + .bit(2, SingleRackChannelId.PROTECT_FLAG_STATUS_CHARGE_OVER_CURRENT) // + .bit(1, SingleRackChannelId.PROTECT_FLAG_STATUS_SYSTEM_OVER_VOLTAGE) // + .bit(0, SingleRackChannelId.PROTECT_FLAG_STATUS_CELL_OVER_VOLTAGE) // + ), // + m(SingleRackChannelId.ALARM_FLAG_REGISTER_1, new UnsignedWordElement(0x2009)), // + m(SingleRackChannelId.ALARM_FLAG_REGISTER_2, new UnsignedWordElement(0x200A)), // + m(SingleRackChannelId.PROTECT_FLAG_REGISTER_1, new UnsignedWordElement(0x200B)), // + m(SingleRackChannelId.PROTECT_FLAG_REGISTER_2, new UnsignedWordElement(0x200C)), // + m(SingleRackChannelId.SHORT_CIRCUIT_FUNCTION, new UnsignedWordElement(0x200D)), // + m(SingleRackChannelId.TESTING_IO, new UnsignedWordElement(0x200E)), // + m(SingleRackChannelId.SOFT_SHUTDOWN, new UnsignedWordElement(0x200F)), // + m(SingleRackChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)), // + m(SingleRackChannelId.CURRENT_BOX_SELF_CALIBRATION, new UnsignedWordElement(0x2011)), // + m(SingleRackChannelId.PCS_ALARM_RESET, new UnsignedWordElement(0x2012)), // + m(SingleRackChannelId.INSULATION_SENSOR_FUNCTION, new UnsignedWordElement(0x2013)), // + m(SingleRackChannelId.AUTO_SET_SLAVES_ID, new UnsignedWordElement(0x2014)), // + new DummyRegisterElement(0x2015, 0x2018), // + m(SingleRackChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID, new UnsignedWordElement(0x2019)), // + m(SingleRackChannelId.TRANSPARENT_MASTER, new UnsignedWordElement(0x201A)), // + m(SingleRackChannelId.SET_EMS_ADDRESS, new UnsignedWordElement(0x201B)), // + m(SingleRackChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x201C)), // + m(SingleRackChannelId.SLEEP, new UnsignedWordElement(0x201D)), // + m(SingleRackChannelId.VOLTAGE_LOW_PROTECTION, new UnsignedWordElement(0x201E)) // + ), // + + // Voltage ranges + new FC3ReadRegistersTask(0x2082, Priority.LOW, // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2082), + ElementToChannelConverter.SCALE_FACTOR_2), // + new DummyRegisterElement(0x2083, 0x2087), + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2088), + ElementToChannelConverter.SCALE_FACTOR_2) // + ), + + // Summary state + new FC3ReadRegistersTask(0x2100, Priority.LOW, + m(SingleRackChannelId.CLUSTER_1_VOLTAGE, new UnsignedWordElement(0x2100), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.CLUSTER_1_CURRENT, new UnsignedWordElement(0x2101), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.CLUSTER_1_CHARGE_INDICATION, new UnsignedWordElement(0x2102)), + m(Battery.ChannelId.SOC, new UnsignedWordElement(0x2103)), + m(SingleRackChannelId.CLUSTER_1_SOH, new UnsignedWordElement(0x2104)), + m(SingleRackChannelId.CLUSTER_1_MAX_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2105)), // + m(SingleRackChannelId.CLUSTER_1_MAX_CELL_VOLTAGE, new UnsignedWordElement(0x2106)), // + m(SingleRackChannelId.CLUSTER_1_MIN_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2107)), // + m(SingleRackChannelId.CLUSTER_1_MIN_CELL_VOLTAGE, new UnsignedWordElement(0x2108)), // + m(SingleRackChannelId.CLUSTER_1_MAX_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x2109)), // + m(SingleRackChannelId.CLUSTER_1_MAX_CELL_TEMPERATURE, new UnsignedWordElement(0x210A)), // + m(SingleRackChannelId.CLUSTER_1_MIN_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x210B)), // + m(SingleRackChannelId.CLUSTER_1_MIN_CELL_TEMPERATURE, new UnsignedWordElement(0x210C)), // + m(SingleRackChannelId.MAX_CELL_RESISTANCE_ID, new UnsignedWordElement(0x210D)), // + m(SingleRackChannelId.MAX_CELL_RESISTANCE, new UnsignedWordElement(0x210E), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(SingleRackChannelId.MIN_CELL_RESISTANCE_ID, new UnsignedWordElement(0x210F)), // + m(SingleRackChannelId.MIN_CELL_RESISTANCE, new UnsignedWordElement(0x2110), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(SingleRackChannelId.POSITIVE_INSULATION, new UnsignedWordElement(0x2111)), // + m(SingleRackChannelId.NEGATIVE_INSULATION, new UnsignedWordElement(0x2112)), // + m(SingleRackChannelId.MAIN_CONTACTOR_FLAG, new UnsignedWordElement(0x2113)), // + new DummyRegisterElement(0x2114), + m(SingleRackChannelId.ENVIRONMENT_TEMPERATURE, new UnsignedWordElement(0x2115)), // + m(SingleRackChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x2116)), // + m(SingleRackChannelId.CELL_VOLTAGE_DIFFERENCE, new UnsignedWordElement(0x2117)), // + m(SingleRackChannelId.TOTAL_VOLTAGE_DIFFERENCE, new UnsignedWordElement(0x2118), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.POWER_TEMPERATURE, new UnsignedWordElement(0x2119)), // + m(SingleRackChannelId.POWER_SUPPLY_VOLTAGE, new UnsignedWordElement(0x211A)) // + ), + + // Critical state + new FC3ReadRegistersTask(0x2140, Priority.HIGH, // + m(new BitsWordElement(0x2140, this) // + .bit(0, SingleRackChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_HIGH) // + .bit(1, SingleRackChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH) // + .bit(2, SingleRackChannelId.ALARM_LEVEL_2_CHA_CURRENT_HIGH) // + .bit(3, SingleRackChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_LOW) // + .bit(4, SingleRackChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW) // + .bit(5, SingleRackChannelId.ALARM_LEVEL_2_DISCHA_CURRENT_HIGH) // + .bit(6, SingleRackChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH) // + .bit(7, SingleRackChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_LOW) // + .bit(8, SingleRackChannelId.ALARM_LEVEL_2_SOC_LOW) // + .bit(9, SingleRackChannelId.ALARM_LEVEL_2_TEMPERATURE_DIFFERENCE_HIGH) // + .bit(10, SingleRackChannelId.ALARM_LEVEL_2_POLES_TEMPERATURE_DIFFERENCE_HIGH) // + .bit(11, SingleRackChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_DIFFERENCE_HIGH) // + .bit(12, SingleRackChannelId.ALARM_LEVEL_2_INSULATION_LOW) // + .bit(13, SingleRackChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_DIFFERENCE_HIGH) // + .bit(14, SingleRackChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH) // + .bit(15, SingleRackChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW) // + ), // + m(new BitsWordElement(0x2141, this) // + .bit(0, SingleRackChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_HIGH) // + .bit(1, SingleRackChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH) // + .bit(2, SingleRackChannelId.ALARM_LEVEL_1_CHA_CURRENT_HIGH) // + .bit(3, SingleRackChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_LOW) // + .bit(4, SingleRackChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW) // + .bit(5, SingleRackChannelId.ALARM_LEVEL_1_DISCHA_CURRENT_HIGH) // + .bit(6, SingleRackChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH) // + .bit(7, SingleRackChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_LOW) // + .bit(8, SingleRackChannelId.ALARM_LEVEL_1_SOC_LOW) // + .bit(9, SingleRackChannelId.ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH) // + .bit(10, SingleRackChannelId.ALARM_LEVEL_1_POLE_TEMPERATURE_TOO_HIGH) // + .bit(11, SingleRackChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH) // + .bit(12, SingleRackChannelId.ALARM_LEVEL_1_INSULATION_LOW) // + .bit(13, SingleRackChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH) // + .bit(14, SingleRackChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH) // + .bit(15, SingleRackChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW) // + ), // + m(SingleRackChannelId.CLUSTER_RUN_STATE, new UnsignedWordElement(0x2142)), // + + m(SingleRackChannelId.MAXIMUM_CELL_VOLTAGE_NUMBER_WHEN_ALARM, new UnsignedWordElement(0x2143)), // + m(SingleRackChannelId.MAXIMUM_CELL_VOLTAGE_WHEN_ALARM, new UnsignedWordElement(0x2144)), // + m(SingleRackChannelId.MAXIMUM_CELL_VOLTAGE_NUMBER_WHEN_STOPPED, new UnsignedWordElement(0x2145)), // + m(SingleRackChannelId.MAXIMUM_CELL_VOLTAGE_WHEN_STOPPED, new UnsignedWordElement(0x2146)), // + m(SingleRackChannelId.MINIMUM_CELL_VOLTAGE_NUMBER_WHEN_ALARM, new UnsignedWordElement(0x2147)), // + m(SingleRackChannelId.MINIMUM_CELL_VOLTAGE_WHEN_ALARM, new UnsignedWordElement(0x2148)), // + m(SingleRackChannelId.MINIMUM_CELL_VOLTAGE_NUMBER_WHEN_STOPPED, new UnsignedWordElement(0x2149)), // + m(SingleRackChannelId.MINIMUM_CELL_VOLTAGE_WHEN_STOPPED, new UnsignedWordElement(0x214A)), // + m(SingleRackChannelId.OVER_VOLTAGE_VALUE_WHEN_ALARM, new UnsignedWordElement(0x214B)), // + m(SingleRackChannelId.OVER_VOLTAGE_VALUE_WHEN_STOPPED, new UnsignedWordElement(0x214C)), // + m(SingleRackChannelId.UNDER_VOLTAGE_VALUE_WHEN_ALARM, new UnsignedWordElement(0x214D)), // + m(SingleRackChannelId.UNDER_VOLTAGE_VALUE_WHEN_STOPPED, new UnsignedWordElement(0x214E)), // + m(SingleRackChannelId.OVER_CHARGE_CURRENT_WHEN_ALARM, new UnsignedWordElement(0x214F)), // + m(SingleRackChannelId.OVER_CHARGE_CURRENT_WHEN_STOPPED, new UnsignedWordElement(0x2150)), // + m(SingleRackChannelId.OVER_DISCHARGE_CURRENT_WHEN_ALARM, new UnsignedWordElement(0x2151)), // + m(SingleRackChannelId.OVER_DISCHARGE_CURRENT_WHEN_STOPPED, new UnsignedWordElement(0x2152)), // + m(SingleRackChannelId.NUMBER_OF_TEMPERATURE_WHEN_ALARM, new UnsignedWordElement(0x2153)), // + new DummyRegisterElement(0x2154, 0x215A), // + m(SingleRackChannelId.OTHER_ALARM_EQUIPMENT_FAILURE, new UnsignedWordElement(0x215B)), // + new DummyRegisterElement(0x215C, 0x215F), // + m(SingleRackChannelId.SYSTEM_MAX_CHARGE_CURRENT, new UnsignedWordElement(0x2160), + ElementToChannelConverter.SCALE_FACTOR_2), // TODO Check if correct! + m(SingleRackChannelId.SYSTEM_MAX_DISCHARGE_CURRENT, new UnsignedWordElement(0x2161), + ElementToChannelConverter.SCALE_FACTOR_2) // TODO Check if correct! + ), // + + // Cluster info + new FC3ReadRegistersTask(0x2180, Priority.LOW, // + m(SingleRackChannelId.CYCLE_TIME, new UnsignedWordElement(0x2180)), // + m(SingleRackChannelId.TOTAL_CAPACITY_HIGH_BITS, new UnsignedWordElement(0x2181)), // + m(SingleRackChannelId.TOTAL_CAPACITY_LOW_BITS, new UnsignedWordElement(0x2182)), // + m(new BitsWordElement(0x2183, this) // + .bit(3, SingleRackChannelId.SLAVE_20_COMMUNICATION_ERROR)// + .bit(2, SingleRackChannelId.SLAVE_19_COMMUNICATION_ERROR)// + .bit(1, SingleRackChannelId.SLAVE_18_COMMUNICATION_ERROR)// + .bit(0, SingleRackChannelId.SLAVE_17_COMMUNICATION_ERROR)// + ), // + m(new BitsWordElement(0x2184, this) // + .bit(15, SingleRackChannelId.SLAVE_16_COMMUNICATION_ERROR)// + .bit(14, SingleRackChannelId.SLAVE_15_COMMUNICATION_ERROR)// + .bit(13, SingleRackChannelId.SLAVE_14_COMMUNICATION_ERROR)// + .bit(12, SingleRackChannelId.SLAVE_13_COMMUNICATION_ERROR)// + .bit(11, SingleRackChannelId.SLAVE_12_COMMUNICATION_ERROR)// + .bit(10, SingleRackChannelId.SLAVE_11_COMMUNICATION_ERROR)// + .bit(9, SingleRackChannelId.SLAVE_10_COMMUNICATION_ERROR)// + .bit(8, SingleRackChannelId.SLAVE_9_COMMUNICATION_ERROR)// + .bit(7, SingleRackChannelId.SLAVE_8_COMMUNICATION_ERROR)// + .bit(6, SingleRackChannelId.SLAVE_7_COMMUNICATION_ERROR)// + .bit(5, SingleRackChannelId.SLAVE_6_COMMUNICATION_ERROR)// + .bit(4, SingleRackChannelId.SLAVE_5_COMMUNICATION_ERROR)// + .bit(3, SingleRackChannelId.SLAVE_4_COMMUNICATION_ERROR)// + .bit(2, SingleRackChannelId.SLAVE_3_COMMUNICATION_ERROR)// + .bit(1, SingleRackChannelId.SLAVE_2_COMMUNICATION_ERROR)// + .bit(0, SingleRackChannelId.SLAVE_1_COMMUNICATION_ERROR)// + ), // + m(new BitsWordElement(0x2185, this) // + .bit(0, SingleRackChannelId.FAILURE_SAMPLING_WIRE)// + .bit(1, SingleRackChannelId.FAILURE_CONNECTOR_WIRE)// + .bit(2, SingleRackChannelId.FAILURE_LTC6803)// + .bit(3, SingleRackChannelId.FAILURE_VOLTAGE_SAMPLING)// + .bit(4, SingleRackChannelId.FAILURE_TEMP_SAMPLING)// + .bit(5, SingleRackChannelId.FAILURE_TEMP_SENSOR)// + .bit(6, SingleRackChannelId.FAILURE_GR_T)// + .bit(7, SingleRackChannelId.FAILURE_PCB)// + .bit(8, SingleRackChannelId.FAILURE_BALANCING_MODULE)// + .bit(9, SingleRackChannelId.FAILURE_TEMP_SAMPLING_LINE)// + .bit(10, SingleRackChannelId.FAILURE_INTRANET_COMMUNICATION)// + .bit(11, SingleRackChannelId.FAILURE_EEPROM)// + .bit(12, SingleRackChannelId.FAILURE_INITIALIZATION)// + ), // + m(SingleRackChannelId.SYSTEM_TIME_HIGH, new UnsignedWordElement(0x2186)), // + m(SingleRackChannelId.SYSTEM_TIME_LOW, new UnsignedWordElement(0x2187)), // + new DummyRegisterElement(0x2188, 0x218E), // + m(SingleRackChannelId.LAST_TIME_CHARGE_CAPACITY_LOW_BITS, new UnsignedWordElement(0x218F), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(SingleRackChannelId.LAST_TIME_CHARGE_END_TIME_HIGH_BITS, new UnsignedWordElement(0x2190)), // + m(SingleRackChannelId.LAST_TIME_CHARGE_END_TIME_LOW_BITS, new UnsignedWordElement(0x2191)), // + new DummyRegisterElement(0x2192), // + m(SingleRackChannelId.LAST_TIME_DISCHARGE_CAPACITY_LOW_BITS, new UnsignedWordElement(0x2193), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(SingleRackChannelId.LAST_TIME_DISCHARGE_END_TIME_HIGH_BITS, new UnsignedWordElement(0x2194)), // + m(SingleRackChannelId.LAST_TIME_DISCHARGE_END_TIME_LOW_BITS, new UnsignedWordElement(0x2195)), // + m(SingleRackChannelId.CELL_OVER_VOLTAGE_STOP_TIMES, new UnsignedWordElement(0x2196)), // + m(SingleRackChannelId.BATTERY_OVER_VOLTAGE_STOP_TIMES, new UnsignedWordElement(0x2197)), // + m(SingleRackChannelId.BATTERY_CHARGE_OVER_CURRENT_STOP_TIMES, new UnsignedWordElement(0x2198)), // + m(SingleRackChannelId.CELL_VOLTAGE_LOW_STOP_TIMES, new UnsignedWordElement(0x2199)), // + m(SingleRackChannelId.BATTERY_VOLTAGE_LOW_STOP_TIMES, new UnsignedWordElement(0x219A)), // + m(SingleRackChannelId.BATTERY_DISCHARGE_OVER_CURRENT_STOP_TIMES, new UnsignedWordElement(0x219B)), // + m(SingleRackChannelId.BATTERY_OVER_TEMPERATURE_STOP_TIMES, new UnsignedWordElement(0x219C)), // + m(SingleRackChannelId.BATTERY_TEMPERATURE_LOW_STOP_TIMES, new UnsignedWordElement(0x219D)), // + m(SingleRackChannelId.CELL_OVER_VOLTAGE_ALARM_TIMES, new UnsignedWordElement(0x219E)), // + m(SingleRackChannelId.BATTERY_OVER_VOLTAGE_ALARM_TIMES, new UnsignedWordElement(0x219F)), // + m(SingleRackChannelId.BATTERY_CHARGE_OVER_CURRENT_ALARM_TIMES, new UnsignedWordElement(0x21A0)), // + m(SingleRackChannelId.CELL_VOLTAGE_LOW_ALARM_TIMES, new UnsignedWordElement(0x21A1)), // + m(SingleRackChannelId.BATTERY_VOLTAGE_LOW_ALARM_TIMES, new UnsignedWordElement(0x21A2)), // + m(SingleRackChannelId.BATTERY_DISCHARGE_OVER_CURRENT_ALARM_TIMES, + new UnsignedWordElement(0x21A3)), // + m(SingleRackChannelId.BATTERY_OVER_TEMPERATURE_ALARM_TIMES, new UnsignedWordElement(0x21A4)), // + m(SingleRackChannelId.BATTERY_TEMPERATURE_LOW_ALARM_TIMES, new UnsignedWordElement(0x21A5)), // + m(SingleRackChannelId.SYSTEM_SHORT_CIRCUIT_PROTECTION_TIMES, new UnsignedWordElement(0x21A6)), // + m(SingleRackChannelId.SYSTEM_GR_OVER_TEMPERATURE_STOP_TIMES, new UnsignedWordElement(0x21A7)), // + new DummyRegisterElement(0x21A8), // + m(SingleRackChannelId.SYSTEM_GR_OVER_TEMPERATURE_ALARM_TIMES, new UnsignedWordElement(0x21A9)), // + new DummyRegisterElement(0x21AA), // + m(SingleRackChannelId.BATTERY_VOLTAGE_DIFFERENCE_ALARM_TIMES, new UnsignedWordElement(0x21AB)), // + m(SingleRackChannelId.BATTERY_VOLTAGE_DIFFERENCE_STOP_TIMES, new UnsignedWordElement(0x21AC)), // + new DummyRegisterElement(0x21AD, 0x21B3), // + m(SingleRackChannelId.SLAVE_TEMPERATURE_COMMUNICATION_ERROR_HIGH, + new UnsignedWordElement(0x21B4)), // + m(SingleRackChannelId.SLAVE_TEMPERATURE_COMMUNICATION_ERROR_LOW, new UnsignedWordElement(0x21B5)) // + ) // + ); // + + if (!config.ReduceTasks()) { + + // Add tasks to read/write work and warn parameters + // Stop parameter + Task writeStopParameters = new FC16WriteRegistersTask(0x2040, // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2040)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2041)), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2042), + ElementToChannelConverter.SCALE_FACTOR_2), // TODO + // Check if + // correct! + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2043), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_PROTECTION, + new UnsignedWordElement(0x2044), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x2045), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2046)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2048), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2049), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_PROTECTION, + new UnsignedWordElement(0x204A), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x204B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_PROTECTION, + new UnsignedWordElement(0x204C)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204D)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_PROTECTION, + new UnsignedWordElement(0x204E)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204F)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION, new UnsignedWordElement(0x2050)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION_RECOVER, new UnsignedWordElement(0x2051)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION, new UnsignedWordElement(0x2052)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION_RECOVER, new UnsignedWordElement(0x2053)), // + m(SingleRackChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION, + new UnsignedWordElement(0x2054)), // + m(SingleRackChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION_RECOVER, + new UnsignedWordElement(0x2055)), // + m(SingleRackChannelId.STOP_PARAMETER_INSULATION_PROTECTION, new UnsignedWordElement(0x2056)), // + m(SingleRackChannelId.STOP_PARAMETER_INSULATION_PROTECTION_RECOVER, new UnsignedWordElement(0x2057)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION, + new UnsignedWordElement(0x2058)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, + new UnsignedWordElement(0x2059)), // + m(SingleRackChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION, + new UnsignedWordElement(0x205A), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, + new UnsignedWordElement(0x205B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION, + new UnsignedWordElement(0x205C)), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION_RECOVER, + new UnsignedWordElement(0x205D)), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION, + new UnsignedWordElement(0x205E)), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION_RECOVER, + new UnsignedWordElement(0x205F)), // + m(SingleRackChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION, + new UnsignedWordElement(0x2060)), // + m(SingleRackChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION_RECOVER, + new UnsignedWordElement(0x2061)) // + ); + +// //Warn parameter + Task writeWarnParameters = new FC16WriteRegistersTask(0x2080, // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2080)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2081)), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2082), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2083), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_ALARM, + new UnsignedWordElement(0x2084), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x2085), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2086)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2087)), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2088), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2089), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_ALARM, + new UnsignedWordElement(0x208A), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x208B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208C)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208D)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208E)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208F)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_LOW_ALARM, new UnsignedWordElement(0x2090)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_LOW_ALARM_RECOVER, new UnsignedWordElement(0x2091)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_HIGH_ALARM, new UnsignedWordElement(0x2092)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_HIGH_ALARM_RECOVER, new UnsignedWordElement(0x2093)), // + m(SingleRackChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM, + new UnsignedWordElement(0x2094)), // + m(SingleRackChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM_RECOVER, + new UnsignedWordElement(0x2095)), // + m(SingleRackChannelId.WARN_PARAMETER_INSULATION_ALARM, new UnsignedWordElement(0x2096)), // + m(SingleRackChannelId.WARN_PARAMETER_INSULATION_ALARM_RECOVER, new UnsignedWordElement(0x2097)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x2098)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, + new UnsignedWordElement(0x2099)), // + m(SingleRackChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x209A), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, + new UnsignedWordElement(0x209B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM, + new UnsignedWordElement(0x209C)), // + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM_RECOVER, + new UnsignedWordElement(0x209D)), // + new DummyRegisterElement(0x209E), + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM, + new UnsignedWordElement(0x209F)), // + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM_RECOVER, + new UnsignedWordElement(0x20A0)), // + m(SingleRackChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM, new UnsignedWordElement(0x20A1)), // + m(SingleRackChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM_RECOVER, + new UnsignedWordElement(0x20A2)) // + ); + + // Stop parameter + Task readStopParameters = new FC3ReadRegistersTask(0x2040, Priority.LOW, // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2040)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2041)), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2042), + ElementToChannelConverter.SCALE_FACTOR_2), // TODO + // Check if + // correct! + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2043), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_PROTECTION, + new UnsignedWordElement(0x2044), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x2045), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2046)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2048), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2049), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_PROTECTION, + new UnsignedWordElement(0x204A), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x204B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_PROTECTION, + new UnsignedWordElement(0x204C)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204D)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_PROTECTION, + new UnsignedWordElement(0x204E)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204F)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION, new UnsignedWordElement(0x2050)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION_RECOVER, new UnsignedWordElement(0x2051)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION, new UnsignedWordElement(0x2052)), // + m(SingleRackChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION_RECOVER, new UnsignedWordElement(0x2053)), // + m(SingleRackChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION, + new UnsignedWordElement(0x2054)), // + m(SingleRackChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION_RECOVER, + new UnsignedWordElement(0x2055)), // + m(SingleRackChannelId.STOP_PARAMETER_INSULATION_PROTECTION, new UnsignedWordElement(0x2056)), // + m(SingleRackChannelId.STOP_PARAMETER_INSULATION_PROTECTION_RECOVER, new UnsignedWordElement(0x2057)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION, + new UnsignedWordElement(0x2058)), // + m(SingleRackChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, + new UnsignedWordElement(0x2059)), // + m(SingleRackChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION, + new UnsignedWordElement(0x205A), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, + new UnsignedWordElement(0x205B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION, + new UnsignedWordElement(0x205C)), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION_RECOVER, + new UnsignedWordElement(0x205D)), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION, + new UnsignedWordElement(0x205E)), // + m(SingleRackChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION_RECOVER, + new UnsignedWordElement(0x205F)), // + m(SingleRackChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION, + new UnsignedWordElement(0x2060)), // + m(SingleRackChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION_RECOVER, + new UnsignedWordElement(0x2061)) // + ); + +// // Warn parameter + Task readWarnParameters = new FC3ReadRegistersTask(0x2080, Priority.LOW, // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2080)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2081)), // + new DummyRegisterElement(0x2082), + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2083), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_ALARM, + new UnsignedWordElement(0x2084), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x2085), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2086)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2087)), // + new DummyRegisterElement(0x2088), + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2089), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_ALARM, + new UnsignedWordElement(0x208A), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, + new UnsignedWordElement(0x208B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208C)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208D)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208E)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208F)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_LOW_ALARM, new UnsignedWordElement(0x2090)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_LOW_ALARM_RECOVER, new UnsignedWordElement(0x2091)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_HIGH_ALARM, new UnsignedWordElement(0x2092)), // + m(SingleRackChannelId.WARN_PARAMETER_SOC_HIGH_ALARM_RECOVER, new UnsignedWordElement(0x2093)), // + m(SingleRackChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM, + new UnsignedWordElement(0x2094)), // + m(SingleRackChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM_RECOVER, + new UnsignedWordElement(0x2095)), // + m(SingleRackChannelId.WARN_PARAMETER_INSULATION_ALARM, new UnsignedWordElement(0x2096)), // + m(SingleRackChannelId.WARN_PARAMETER_INSULATION_ALARM_RECOVER, new UnsignedWordElement(0x2097)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x2098)), // + m(SingleRackChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, + new UnsignedWordElement(0x2099)), // + m(SingleRackChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x209A), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, + new UnsignedWordElement(0x209B), ElementToChannelConverter.SCALE_FACTOR_2), // + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM, + new UnsignedWordElement(0x209C)), // + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM_RECOVER, + new UnsignedWordElement(0x209D)), // + new DummyRegisterElement(0x209E), + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM, + new UnsignedWordElement(0x209F)), // + m(SingleRackChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM_RECOVER, + new UnsignedWordElement(0x20A0)), // + m(SingleRackChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM, new UnsignedWordElement(0x20A1)), // + m(SingleRackChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM_RECOVER, + new UnsignedWordElement(0x20A2)) // + ); + + protocol.addTask(readStopParameters); + protocol.addTask(readWarnParameters); + protocol.addTask(writeStopParameters); + protocol.addTask(writeWarnParameters); + + // Add tasks for cell voltages and temperatures according to the number of + // slaves, one task per module is created + // Cell voltages + int offset = ModuleParameters.ADDRESS_OFFSET.getValue(); + int voltOffset = ModuleParameters.VOLTAGE_ADDRESS_OFFSET.getValue(); + int voltSensors = ModuleParameters.VOLTAGE_SENSORS_PER_MODULE.getValue(); + for (int i = 0; i < this.config.numberOfSlaves(); i++) { + Collection> elements = new ArrayList<>(); + for (int j = i * voltSensors; j < (i + 1) * voltSensors; j++) { + String key = getSingleCellPrefix(j) + KEY_VOLTAGE; + UnsignedWordElement uwe = new UnsignedWordElement(offset + voltOffset + j); + AbstractModbusElement ame = m(channelMap.get(key).channelId(), uwe); + elements.add(ame); + } + protocol.addTask(new FC3ReadRegistersTask(offset + voltOffset + i * voltSensors, Priority.LOW, + elements.toArray(new AbstractModbusElement[0]))); + } + + // Cell temperatures + int tempOffset = ModuleParameters.TEMPERATURE_ADDRESS_OFFSET.getValue(); + int tempSensors = ModuleParameters.TEMPERATURE_SENSORS_PER_MODULE.getValue(); + for (int i = 0; i < this.config.numberOfSlaves(); i++) { + Collection> elements = new ArrayList<>(); + for (int j = i * tempSensors; j < (i + 1) * tempSensors; j++) { + String key = getSingleCellPrefix(j) + KEY_TEMPERATURE; + SignedWordElement swe = new SignedWordElement(offset + tempOffset + j); + AbstractModbusElement ame = m(channelMap.get(key).channelId(), swe); + elements.add(ame); + } + protocol.addTask(new FC3ReadRegistersTask(offset + tempOffset + i * tempSensors, Priority.LOW, + elements.toArray(new AbstractModbusElement[0]))); + } + } + + return protocol; + } +} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/VersionBChannelId.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/SingleRackChannelId.java similarity index 96% rename from io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/VersionBChannelId.java rename to io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/SingleRackChannelId.java index 90b3a9eac42..7fbedbb580a 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/VersionBChannelId.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/SingleRackChannelId.java @@ -1,23 +1,23 @@ -package io.openems.edge.battery.soltaro.versionb; +package io.openems.edge.battery.soltaro.single.versionb; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.AutoSetFunction; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.ChargeIndication; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.ClusterRunState; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.ContactExport; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.ContactorControl; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.ContactorState; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.FanStatus; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.PreContactorState; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.ShortCircuitFunction; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.SystemRunMode; -import io.openems.edge.common.channel.AccessMode; -import io.openems.edge.common.channel.ChannelId; +import io.openems.edge.battery.soltaro.single.versionb.Enums.AutoSetFunction; +import io.openems.edge.battery.soltaro.single.versionb.Enums.ChargeIndication; +import io.openems.edge.battery.soltaro.single.versionb.Enums.ClusterRunState; +import io.openems.edge.battery.soltaro.single.versionb.Enums.ContactExport; +import io.openems.edge.battery.soltaro.single.versionb.Enums.ContactorControl; +import io.openems.edge.battery.soltaro.single.versionb.Enums.ContactorState; +import io.openems.edge.battery.soltaro.single.versionb.Enums.FanStatus; +import io.openems.edge.battery.soltaro.single.versionb.Enums.PreContactorState; +import io.openems.edge.battery.soltaro.single.versionb.Enums.ShortCircuitFunction; +import io.openems.edge.battery.soltaro.single.versionb.Enums.SystemRunMode; +import io.openems.edge.battery.soltaro.State; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; -public enum VersionBChannelId implements ChannelId { +public enum SingleRackChannelId implements io.openems.edge.common.channel.ChannelId { // IntegerWriteChannels BMS_CONTACTOR_CONTROL(Doc.of(ContactorControl.values()) // .accessMode(AccessMode.READ_WRITE)), // @@ -474,7 +474,7 @@ public enum VersionBChannelId implements ChannelId { .text("Alarm flag status charge over current")), // ALARM_FLAG_STATUS_SYSTEM_OVER_VOLTAGE(Doc.of(Level.OK) // .text("Alarm flag status system over voltage")), // - ALARM_FLAG_STATUS_CELL_OVER_VOLTAGE(Doc.of(Level.FAULT) // + ALARM_FLAG_STATUS_CELL_OVER_VOLTAGE(Doc.of(Level.OK) // .text("Alarm flag status cell over voltage")), // PROTECT_FLAG_STATUS_DISCHARGE_TEMPERATURE_LOW(Doc.of(Level.OK) // .text("Protect flag status discharge temperature low")), // @@ -699,7 +699,7 @@ public enum VersionBChannelId implements ChannelId { private final Doc doc; - private VersionBChannelId(Doc doc) { + private SingleRackChannelId(Doc doc) { this.doc = doc; } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/package-info.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/package-info.java new file mode 100644 index 00000000000..853fe6b9cf2 --- /dev/null +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.edge.battery.soltaro.single.versionb; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/BatteryState.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/BatteryState.java deleted file mode 100644 index 0da93231bd7..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/BatteryState.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.openems.edge.battery.soltaro.versionb; - -public enum BatteryState { - - DEFAULT, - ON, - OFF, - CONFIGURE_SLAVES -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ErrorCode.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ErrorCode.java deleted file mode 100644 index d71e927ad31..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/ErrorCode.java +++ /dev/null @@ -1,104 +0,0 @@ -package io.openems.edge.battery.soltaro.versionb; - -import io.openems.edge.common.channel.OptionsEnum; - -public enum ErrorCode implements OptionsEnum { - UNDEFINED(-1, "UNDEFINED", null), // - - CELL_VOLTAGE_HIGH(0, "Cell voltage high", VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_HIGH), // - TOTAL_VOLTAGE_HIGH(0, "Total voltage high", VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH), // - CHARGE_CURRENT_HIGH(2, "Charge current high", VersionBChannelId.ALARM_LEVEL_2_CHA_CURRENT_HIGH), // - CELL_VOLTAGE_LOW(3, "Cell voltage low", VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_LOW), // - TOTAL_VOLTAGE_LOW(4, "Total voltage low", VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW), // - DISCHARGE_CURRENT_HIGH(5, "Discharge current high", VersionBChannelId.ALARM_LEVEL_2_DISCHA_CURRENT_HIGH), // - CELL_CHARGE_TEMPERATURE_HIGH(6, "Cell charge temperature high", VersionBChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH), // - CELL_CHARGE_TEMPERATURE_LOW(7, "Cell charge temperature low", VersionBChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_LOW), // - SOC_LOW(8, "SoC low", VersionBChannelId.ALARM_LEVEL_2_SOC_LOW), // - TEMPERATURE_DIFFERENCE_HIGH(9, "Temperature difference high", - VersionBChannelId.ALARM_LEVEL_2_TEMPERATURE_DIFFERENCE_HIGH), // - POLES_TEMPERATURE_DIFFERENCE_HIGH(10, "Poles temperature difference high", - VersionBChannelId.ALARM_LEVEL_2_POLES_TEMPERATURE_DIFFERENCE_HIGH), // - CELL_VOLTAGE_DIFFERENCE_HIGH(11, "Cell voltage difference high", - VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_DIFFERENCE_HIGH), // - INSULATION_LOW(12, "Insulation low", VersionBChannelId.ALARM_LEVEL_2_INSULATION_LOW), // - TOTAL_VOLTAGE_DIFFERENCE_HIGH(13, "Total voltage difference high", - VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_DIFFERENCE_HIGH), // - CELL_DISCHARGE_TEMPERATURE_HIGH(14, "Cell discharge temperature high", - VersionBChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH), // - CELL_DISCHARGE_TEMPERATURE_LOW(15, "Cell discharge temperature low", - VersionBChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW), // - - SAMPLING_WIRE(16, "Sampling wire", VersionBChannelId.FAILURE_SAMPLING_WIRE), // - CONNECTOR_WIRE(17, "Connector wire", VersionBChannelId.FAILURE_CONNECTOR_WIRE), // - LTC6803(18, "LTC 6803", VersionBChannelId.FAILURE_LTC6803), // - VOLTAGE_SAMPLING(19, "Voltage sampling", VersionBChannelId.FAILURE_VOLTAGE_SAMPLING), // - TEMP_SAMPLING(20, "Temperature sampling", VersionBChannelId.FAILURE_TEMP_SAMPLING), // - TEMP_SENSOR(21, "Temperature sensor", VersionBChannelId.FAILURE_TEMP_SENSOR), // - GR_T(22, "Gr T", VersionBChannelId.FAILURE_GR_T), // - PCB(23, "PCB", VersionBChannelId.FAILURE_PCB), // - BALANCING_MODULE(24, "Balancing module", VersionBChannelId.FAILURE_BALANCING_MODULE), // - TEMP_SAMPLING_LINE(25, "Temperature sampling line", VersionBChannelId.FAILURE_TEMP_SAMPLING_LINE), // - INTRANET_COMMUNICATION(26, "Intranet communication", VersionBChannelId.FAILURE_INTRANET_COMMUNICATION), // - EEPROM(27, "EEPROM", VersionBChannelId.FAILURE_EEPROM), // - INITIALIZATION(28, "Initialization", VersionBChannelId.FAILURE_INITIALIZATION), // - - SLAVE_20(29, "Communication error slave 20", VersionBChannelId.SLAVE_20_COMMUNICATION_ERROR), // - SLAVE_19(30, "Communication error slave 19", VersionBChannelId.SLAVE_19_COMMUNICATION_ERROR), // - SLAVE_18(31, "Communication error slave 18", VersionBChannelId.SLAVE_18_COMMUNICATION_ERROR), // - SLAVE_17(32, "Communication error slave 17", VersionBChannelId.SLAVE_17_COMMUNICATION_ERROR), // - SLAVE_16(33, "Communication error slave 16", VersionBChannelId.SLAVE_16_COMMUNICATION_ERROR), // - SLAVE_15(34, "Communication error slave 15", VersionBChannelId.SLAVE_15_COMMUNICATION_ERROR), // - SLAVE_14(35, "Communication error slave 14", VersionBChannelId.SLAVE_14_COMMUNICATION_ERROR), // - SLAVE_13(36, "Communication error slave 13", VersionBChannelId.SLAVE_13_COMMUNICATION_ERROR), // - SLAVE_12(37, "Communication error slave 12", VersionBChannelId.SLAVE_12_COMMUNICATION_ERROR), // - SLAVE_11(38, "Communication error slave 11", VersionBChannelId.SLAVE_11_COMMUNICATION_ERROR), // - SLAVE_10(39, "Communication error slave 10", VersionBChannelId.SLAVE_10_COMMUNICATION_ERROR), // - SLAVE_9(40, "Communication error slave 9", VersionBChannelId.SLAVE_9_COMMUNICATION_ERROR), // - SLAVE_8(41, "Communication error slave 8", VersionBChannelId.SLAVE_8_COMMUNICATION_ERROR), // - SLAVE_7(42, "Communication error slave 7", VersionBChannelId.SLAVE_7_COMMUNICATION_ERROR), // - SLAVE_6(43, "Communication error slave 6", VersionBChannelId.SLAVE_6_COMMUNICATION_ERROR), // - SLAVE_5(44, "Communication error slave 5", VersionBChannelId.SLAVE_5_COMMUNICATION_ERROR), // - SLAVE_4(45, "Communication error slave 4", VersionBChannelId.SLAVE_4_COMMUNICATION_ERROR), // - SLAVE_3(46, "Communication error slave 3", VersionBChannelId.SLAVE_3_COMMUNICATION_ERROR), // - SLAVE_2(47, "Communication error slave 2", VersionBChannelId.SLAVE_2_COMMUNICATION_ERROR), // - SLAVE_1(48, "Communication error slave 1", VersionBChannelId.SLAVE_1_COMMUNICATION_ERROR); // - - private ErrorCode(int value, String name, VersionBChannelId errorChannelId) { - this.value = value; - this.name = name; - this.errorChannelId = errorChannelId; - } - - private int value; - private String name; - private VersionBChannelId errorChannelId; - - @Override - public int getValue() { - return value; - } - - @Override - public String getName() { - return name; - } - - public VersionBChannelId getErrorChannelId() { - return errorChannelId; - } - - public static ErrorCode getErrorCode(VersionBChannelId id) { - for (ErrorCode code : ErrorCode.values()) { - if (id.equals(code.errorChannelId)) { - return code; - } - } - return null; - } - - @Override - public OptionsEnum getUndefined() { - return UNDEFINED; - } - -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/SoltaroRackVersionB.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/SoltaroRackVersionB.java deleted file mode 100644 index 63635cb0994..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/SoltaroRackVersionB.java +++ /dev/null @@ -1,1290 +0,0 @@ -package io.openems.edge.battery.soltaro.versionb; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -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.event.Event; -import org.osgi.service.event.EventConstants; -import org.osgi.service.event.EventHandler; -import org.osgi.service.metatype.annotations.Designate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.exceptions.OpenemsException; -import io.openems.edge.battery.api.Battery; -import io.openems.edge.battery.soltaro.multirack.ChannelIdImpl; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.AutoSetFunction; -import io.openems.edge.battery.soltaro.versionb.VersionBEnums.ContactorControl; -import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; -import io.openems.edge.bridge.modbus.api.BridgeModbus; -import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; -import io.openems.edge.bridge.modbus.api.ModbusProtocol; -import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; -import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; -import io.openems.edge.bridge.modbus.api.element.SignedWordElement; -import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; -import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; -import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; -import io.openems.edge.bridge.modbus.api.task.FC6WriteRegisterTask; -import io.openems.edge.bridge.modbus.api.task.Task; -import io.openems.edge.common.channel.Channel; -import io.openems.edge.common.channel.IntegerDoc; -import io.openems.edge.common.channel.IntegerReadChannel; -import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.StateChannel; -import io.openems.edge.common.channel.Unit; -import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.event.EdgeEventConstants; -import io.openems.edge.common.taskmanager.Priority; - -@Designate(ocd = Config.class, factory = true) -@Component( // - name = "Bms.Fenecon.Soltaro.VersionB", // - immediate = true, // - configurationPolicy = ConfigurationPolicy.REQUIRE, // - property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // -) -public class SoltaroRackVersionB extends AbstractOpenemsModbusComponent - implements Battery, OpenemsComponent, EventHandler { // , JsonApi TODO - - protected static final int SYSTEM_ON = 1; - protected final static int SYSTEM_OFF = 0; - - private static final String KEY_TEMPERATURE = "_TEMPERATURE"; - private static final String KEY_VOLTAGE = "_VOLTAGE"; - private static final Integer SYSTEM_RESET = 0x1; - private static final String NUMBER_FORMAT = "%03d"; // creates string number with leading zeros - - @Reference - protected ConfigurationAdmin cm; - - private final Logger log = LoggerFactory.getLogger(SoltaroRackVersionB.class); - private String modbusBridgeId; - private State state = State.UNDEFINED; - // if configuring is needed this is used to go through the necessary steps - private ConfiguringProcess nextConfiguringProcess = ConfiguringProcess.NONE; - private Config config; - private Map> channelMap; - // If an error has occurred, this indicates the time when next action could be - // done - private LocalDateTime errorDelayIsOver = null; - private int unsuccessfulStarts = 0; - private LocalDateTime startAttemptTime = null; - - private LocalDateTime timeAfterAutoId = null; - private LocalDateTime configuringFinished = null; - private int DELAY_AUTO_ID_SECONDS = 5; - private int DELAY_AFTER_CONFIGURING_FINISHED = 5; - - public SoltaroRackVersionB() { - super(// - OpenemsComponent.ChannelId.values(), // - Battery.ChannelId.values(), // - VersionBChannelId.values() // - ); - } - - @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) - protected void setModbus(BridgeModbus modbus) { - super.setModbus(modbus); - } - - @Activate - void activate(ComponentContext context, Config config) { - this.config = config; - - // adds dynamically created channels and save them into a map to access them - // when modbus tasks are created - channelMap = createDynamicChannels(); - - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", - config.modbus_id()); - this.modbusBridgeId = config.modbus_id(); - initializeCallbacks(); - - setWatchdog(config.watchdog()); - setSoCLowAlarm(config.SoCLowAlarm()); - setCapacity(); - } - - private void setCapacity() { - int capacity = this.config.numberOfSlaves() * ModuleParameters.CAPACITY_WH.getValue() / 1000; - this.channel(Battery.ChannelId.CAPACITY).setNextValue(capacity); - } - - private void handleStateMachine() { - log.info("SoltaroRackVersionB.handleStateMachine(): State: " + this.getStateMachineState()); - boolean readyForWorking = false; - switch (this.getStateMachineState()) { - case ERROR: - stopSystem(); - errorDelayIsOver = LocalDateTime.now().plusSeconds(config.errorLevel2Delay()); - setStateMachineState(State.ERRORDELAY); - break; - - case ERRORDELAY: - if (LocalDateTime.now().isAfter(errorDelayIsOver)) { - errorDelayIsOver = null; - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else { - this.setStateMachineState(State.OFF); - } - } - break; - case INIT: - if (this.isSystemIsRunning()) { - this.setStateMachineState(State.RUNNING); - unsuccessfulStarts = 0; - startAttemptTime = null; - } else { - if (startAttemptTime.plusSeconds(config.maxStartTime()).isBefore(LocalDateTime.now())) { - startAttemptTime = null; - unsuccessfulStarts++; - this.stopSystem(); - this.setStateMachineState(State.STOPPING); - if (unsuccessfulStarts >= config.maxStartAppempts()) { - errorDelayIsOver = LocalDateTime.now().plusSeconds(config.startUnsuccessfulDelay()); - this.setStateMachineState(State.ERRORDELAY); - unsuccessfulStarts = 0; - } - } - } - break; - case OFF: - log.debug("in case 'OFF'; try to start the system"); - this.startSystem(); - log.debug("set state to 'INIT'"); - this.setStateMachineState(State.INIT); - startAttemptTime = LocalDateTime.now(); - break; - case RUNNING: - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else { - readyForWorking = true; - } - break; - case STOPPING: - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else { - if (this.isSystemStopped()) { - this.setStateMachineState(State.OFF); - } - } - break; - case UNDEFINED: - if (isSystemStateUndefined()) { // do nothing until state is clearly defined - log.info(" ===>>> STATE is currently undefined! <<<==="); - break; - } - if (this.isError()) { - this.setStateMachineState(State.ERROR); - } else if (this.isSystemStopped()) { - this.setStateMachineState(State.OFF); - } else if (this.isSystemIsRunning()) { - this.setStateMachineState(State.RUNNING); - } - break; - } - - this.getReadyForWorking().setNextValue(readyForWorking); - } - - /* - * creates a map containing channels for voltage and temperature depending on - * the number of modules - */ - private Map> createDynamicChannels() { - Map> map = new HashMap<>(); - - int voltSensors = ModuleParameters.VOLTAGE_SENSORS_PER_MODULE.getValue(); - for (int i = 0; i < this.config.numberOfSlaves(); i++) { - for (int j = i * voltSensors; j < (i + 1) * voltSensors; j++) { - String key = getSingleCellPrefix(j) + KEY_VOLTAGE; - IntegerDoc doc = new IntegerDoc(); - io.openems.edge.common.channel.ChannelId channelId = new ChannelIdImpl(key, doc.unit(Unit.MILLIVOLT)); - IntegerReadChannel integerReadChannel = (IntegerReadChannel) this.addChannel(channelId); - map.put(key, integerReadChannel); - } - } - - int tempSensors = ModuleParameters.TEMPERATURE_SENSORS_PER_MODULE.getValue(); - for (int i = 0; i < this.config.numberOfSlaves(); i++) { - for (int j = i * tempSensors; j < (i + 1) * tempSensors; j++) { - String key = getSingleCellPrefix(j) + KEY_TEMPERATURE; - - IntegerDoc doc = new IntegerDoc(); - io.openems.edge.common.channel.ChannelId channelId = new ChannelIdImpl(key, - doc.unit(Unit.DEZIDEGREE_CELSIUS)); - IntegerReadChannel integerReadChannel = (IntegerReadChannel) this.addChannel(channelId); - map.put(key, integerReadChannel); - } - } - return map; - } - - private String getSingleCellPrefix(int num) { - return "CLUSTER_1_BATTERY_" + String.format(NUMBER_FORMAT, num); - } - - private void setWatchdog(int time_seconds) { - try { - IntegerWriteChannel c = this.channel(VersionBChannelId.EMS_COMMUNICATION_TIMEOUT); - c.setNextWriteValue(time_seconds); - } catch (OpenemsException e) { - log.error("Error while setting ems timeout!\n" + e.getMessage()); - } - } - - @Deactivate - protected void deactivate() { - // Remove dynamically created channels when component is deactivated - for (Channel c : this.channelMap.values()) { - this.removeChannel(c); - } - super.deactivate(); - } - - private void initializeCallbacks() { - - this.channel(VersionBChannelId.CLUSTER_1_VOLTAGE).onChange(value -> { - @SuppressWarnings("unchecked") - Optional vOpt = (Optional) value.asOptional(); - if (!vOpt.isPresent()) { - return; - } - int voltage_volt = (int) (vOpt.get() * 0.001); - log.debug("callback voltage, value: " + voltage_volt); - this.channel(Battery.ChannelId.VOLTAGE).setNextValue(voltage_volt); - }); - - this.channel(VersionBChannelId.CLUSTER_1_MIN_CELL_VOLTAGE).onChange(value -> { - @SuppressWarnings("unchecked") - Optional vOpt = (Optional) value.asOptional(); - if (!vOpt.isPresent()) { - return; - } - int voltage_millivolt = vOpt.get(); - log.debug("callback min cell voltage, value: " + voltage_millivolt); - this.channel(Battery.ChannelId.MIN_CELL_VOLTAGE).setNextValue(voltage_millivolt); - }); - - // write battery ranges to according channels in battery api - // MAX_VOLTAGE x2082 - this.channel(VersionBChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM).onChange(value -> { - @SuppressWarnings("unchecked") - Optional vOpt = (Optional) value.asOptional(); - if (!vOpt.isPresent()) { - return; - } - int max_charge_voltage = (int) (vOpt.get() * 0.001); - log.debug("callback battery range, max charge voltage, value: " + max_charge_voltage); - this.channel(Battery.ChannelId.CHARGE_MAX_VOLTAGE).setNextValue(max_charge_voltage); - }); - - // DISCHARGE_MIN_VOLTAGE 0x2088 - this.channel(VersionBChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM).onChange(value -> { - @SuppressWarnings("unchecked") - Optional vOpt = (Optional) value.asOptional(); - if (!vOpt.isPresent()) { - return; - } - int min_discharge_voltage = (int) (vOpt.get() * 0.001); - log.debug("callback battery range, min discharge voltage, value: " + min_discharge_voltage); - this.channel(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE).setNextValue(min_discharge_voltage); - }); - - // CHARGE_MAX_CURRENT 0x2160 - this.channel(VersionBChannelId.SYSTEM_MAX_CHARGE_CURRENT).onChange(value -> { - @SuppressWarnings("unchecked") - Optional cOpt = (Optional) value.asOptional(); - if (!cOpt.isPresent()) { - return; - } - int max_current = (int) (cOpt.get() * 0.001); - log.debug("callback battery range, max charge current, value: " + max_current); - this.channel(Battery.ChannelId.CHARGE_MAX_CURRENT).setNextValue(max_current); - }); - - // DISCHARGE_MAX_CURRENT 0x2161 - this.channel(VersionBChannelId.SYSTEM_MAX_DISCHARGE_CURRENT).onChange(value -> { - @SuppressWarnings("unchecked") - Optional cOpt = (Optional) value.asOptional(); - if (!cOpt.isPresent()) { - return; - } - int max_current = (int) (cOpt.get() * 0.001); - log.debug("callback battery range, max discharge current, value: " + max_current); - this.channel(Battery.ChannelId.DISCHARGE_MAX_CURRENT).setNextValue(max_current); - }); - - } - - @Override - public void handleEvent(Event event) { - if (!this.isEnabled()) { - return; - } - switch (event.getTopic()) { - - case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: - handleBatteryState(); - break; - } - } - - private void handleBatteryState() { - switch (config.batteryState()) { - case DEFAULT: - handleStateMachine(); - break; - case OFF: - stopSystem(); - break; - case ON: - startSystem(); - break; - case CONFIGURE_SLAVES: - configureSlaves(); - break; - - } - } - - private void configureSlaves() { - if (nextConfiguringProcess == ConfiguringProcess.NONE) { - nextConfiguringProcess = ConfiguringProcess.CONFIGURING_STARTED; - } - - switch (nextConfiguringProcess) { - case CONFIGURING_STARTED: - log.info(" ===> CONFIGURING STARTED: setNumberOfModules() <==="); - setNumberOfModules(); - break; - case SET_ID_AUTO_CONFIGURING: - log.info(" ===> SET_ID_AUTO_CONFIGURING: setIdAutoConfiguring() <==="); - setIdAutoConfiguring(); - break; - case CHECK_ID_AUTO_CONFIGURING: - if (timeAfterAutoId != null) { - if (timeAfterAutoId.plusSeconds(DELAY_AUTO_ID_SECONDS).isAfter(LocalDateTime.now())) { - break; - } else { - timeAfterAutoId = null; - } - } - log.info(" ===> CHECK_ID_AUTO_CONFIGURING: checkIdAutoConfiguring() <==="); - checkIdAutoConfiguring(); - break; - case SET_TEMPERATURE_ID_AUTO_CONFIGURING: - log.info(" ===> SET_TEMPERATURE_ID_AUTO_CONFIGURING: setTemperatureIdAutoConfiguring() <==="); - setTemperatureIdAutoConfiguring(); - break; - case CHECK_TEMPERATURE_ID_AUTO_CONFIGURING: - if (timeAfterAutoId != null) { - if (timeAfterAutoId.plusSeconds(DELAY_AUTO_ID_SECONDS).isAfter(LocalDateTime.now())) { - break; - } else { - timeAfterAutoId = null; - } - } - log.info(" ===> CHECK_TEMPERATURE_ID_AUTO_CONFIGURING: checkTemperatureIdAutoConfiguring() <==="); - checkTemperatureIdAutoConfiguring(); - break; - case SET_VOLTAGE_RANGES: - log.info(" ===> SET_VOLTAGE_RANGES: setVoltageRanges() <==="); - setVoltageRanges(); - - break; - case CONFIGURING_FINISHED: - log.info("====>>> Configuring successful! <<<===="); - - if (configuringFinished == null) { - nextConfiguringProcess = ConfiguringProcess.RESTART_AFTER_SETTING; - } else { - if (configuringFinished.plusSeconds(DELAY_AFTER_CONFIGURING_FINISHED).isAfter(LocalDateTime.now())) { - log.info(">>> Delay time after configuring!"); - } else { - log.info("Delay time after configuring is over, reset system"); - IntegerWriteChannel resetChannel = this.channel(VersionBChannelId.SYSTEM_RESET); - try { - resetChannel.setNextWriteValue(SYSTEM_RESET); - configuringFinished = null; - } catch (OpenemsException e) { - log.error("Error while trying to reset the system!"); - } - } - } - break; - case RESTART_AFTER_SETTING: - // A manual restart is needed - case NONE: - break; - } - } - - private void setVoltageRanges() { - - try { - IntegerWriteChannel level1OverVoltageChannel = this - .channel(VersionBChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM); - level1OverVoltageChannel.setNextWriteValue( - this.config.numberOfSlaves() * ModuleParameters.LEVEL_1_TOTAL_OVER_VOLTAGE_MILLIVOLT.getValue()); - - IntegerWriteChannel level1OverVoltageChannelRecover = this - .channel(VersionBChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER); - level1OverVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() - * ModuleParameters.LEVEL_1_TOTAL_OVER_VOLTAGE_RECOVER_MILLIVOLT.getValue()); - - IntegerWriteChannel level1LowVoltageChannel = this - .channel(VersionBChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM); - level1LowVoltageChannel.setNextWriteValue( - this.config.numberOfSlaves() * ModuleParameters.LEVEL_1_TOTAL_LOW_VOLTAGE_MILLIVOLT.getValue()); - - IntegerWriteChannel level1LowVoltageChannelRecover = this - .channel(VersionBChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER); - level1LowVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() - * ModuleParameters.LEVEL_1_TOTAL_LOW_VOLTAGE_RECOVER_MILLIVOLT.getValue()); - - IntegerWriteChannel level2OverVoltageChannel = this - .channel(VersionBChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_PROTECTION); - level2OverVoltageChannel.setNextWriteValue( - this.config.numberOfSlaves() * ModuleParameters.LEVEL_2_TOTAL_OVER_VOLTAGE_MILLIVOLT.getValue()); - - IntegerWriteChannel level2OverVoltageChannelRecover = this - .channel(VersionBChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER); - level2OverVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() - * ModuleParameters.LEVEL_2_TOTAL_OVER_VOLTAGE_RECOVER_MILLIVOLT.getValue()); - - IntegerWriteChannel level2LowVoltageChannel = this - .channel(VersionBChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_PROTECTION); - level2LowVoltageChannel.setNextWriteValue( - this.config.numberOfSlaves() * ModuleParameters.LEVEL_2_TOTAL_LOW_VOLTAGE_MILLIVOLT.getValue()); - - IntegerWriteChannel level2LowVoltageChannelRecover = this - .channel(VersionBChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER); - level2LowVoltageChannelRecover.setNextWriteValue(this.config.numberOfSlaves() - * ModuleParameters.LEVEL_2_TOTAL_LOW_VOLTAGE_RECOVER_MILLIVOLT.getValue()); - - nextConfiguringProcess = ConfiguringProcess.CONFIGURING_FINISHED; - configuringFinished = LocalDateTime.now(); - - } catch (OpenemsException e) { - log.error("Setting voltage ranges not successful!"); - } - - } - - private void checkTemperatureIdAutoConfiguring() { - IntegerReadChannel autoSetTemperatureSlavesIdChannel = this - .channel(VersionBChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID); - Optional autoSetTemperatureSlavesIdOpt = autoSetTemperatureSlavesIdChannel.value().asOptional(); - if (!autoSetTemperatureSlavesIdOpt.isPresent()) { - return; - } - int autoSetTemperatureSlaves = autoSetTemperatureSlavesIdOpt.get(); - if (autoSetTemperatureSlaves == VersionBEnums.AutoSetFunction.FAILURE.getValue()) { - log.error("Auto set temperature slaves id failed! Start configuring process again!"); - // Auto set failed, try again - nextConfiguringProcess = ConfiguringProcess.CONFIGURING_STARTED; - } else if (autoSetTemperatureSlaves == VersionBEnums.AutoSetFunction.SUCCES.getValue()) { - log.info("Auto set temperature slaves id succeeded!"); - nextConfiguringProcess = ConfiguringProcess.SET_VOLTAGE_RANGES; - } - } - - private void setTemperatureIdAutoConfiguring() { - - IntegerWriteChannel autoSetSlavesTemperatureIdChannel = this - .channel(VersionBChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID); - try { - autoSetSlavesTemperatureIdChannel.setNextWriteValue(AutoSetFunction.START_AUTO_SETTING.getValue()); - timeAfterAutoId = LocalDateTime.now(); - nextConfiguringProcess = ConfiguringProcess.CHECK_TEMPERATURE_ID_AUTO_CONFIGURING; - } catch (OpenemsException e) { - log.error("Setting temperature id auto set not successful"); // Set was not successful, it will be tried - // until it succeeded - } - } - - private void checkIdAutoConfiguring() { - IntegerReadChannel autoSetSlavesIdChannel = this.channel(VersionBChannelId.AUTO_SET_SLAVES_ID); - Optional autoSetSlavesIdOpt = autoSetSlavesIdChannel.value().asOptional(); - if (!autoSetSlavesIdOpt.isPresent()) { - return; - } - int autoSetSlaves = autoSetSlavesIdOpt.get(); - if (autoSetSlaves == VersionBEnums.AutoSetFunction.FAILURE.getValue()) { - log.error("Auto set slaves id failed! Start configuring process again!"); - // Auto set failed, try again - nextConfiguringProcess = ConfiguringProcess.CONFIGURING_STARTED; - } else if (autoSetSlaves == VersionBEnums.AutoSetFunction.SUCCES.getValue()) { - log.info("Auto set slaves id succeeded!"); - nextConfiguringProcess = ConfiguringProcess.SET_TEMPERATURE_ID_AUTO_CONFIGURING; - } - } - - private void setIdAutoConfiguring() { - // Set number of modules - IntegerWriteChannel autoSetSlavesIdChannel = this.channel(VersionBChannelId.AUTO_SET_SLAVES_ID); - try { - autoSetSlavesIdChannel.setNextWriteValue(AutoSetFunction.START_AUTO_SETTING.getValue()); - timeAfterAutoId = LocalDateTime.now(); - nextConfiguringProcess = ConfiguringProcess.CHECK_ID_AUTO_CONFIGURING; - } catch (OpenemsException e) { - log.error("Setting slave numbers not successful"); // Set was not successful, it will be tried until it - // succeeded - } - } - - private void setNumberOfModules() { - // Set number of modules - IntegerWriteChannel numberOfSlavesChannel = this - .channel(VersionBChannelId.WORK_PARAMETER_PCS_COMMUNICATION_RATE); - try { - numberOfSlavesChannel.setNextWriteValue(this.config.numberOfSlaves()); - nextConfiguringProcess = ConfiguringProcess.SET_ID_AUTO_CONFIGURING; - } catch (OpenemsException e) { - log.error("Setting slave numbers not successful"); // Set was not successful, it will be tried until it - // succeeded - } - } - - private enum ConfiguringProcess { - NONE, CONFIGURING_STARTED, SET_ID_AUTO_CONFIGURING, CHECK_ID_AUTO_CONFIGURING, - SET_TEMPERATURE_ID_AUTO_CONFIGURING, CHECK_TEMPERATURE_ID_AUTO_CONFIGURING, SET_VOLTAGE_RANGES, - CONFIGURING_FINISHED, RESTART_AFTER_SETTING - } - - private boolean isSystemStateUndefined() { // System is undefined if it is definitely not started and not stopped - return (!isSystemIsRunning() && !isSystemStopped()); - } - - private boolean isSystemIsRunning() { - IntegerReadChannel contactorControlChannel = this.channel(VersionBChannelId.BMS_CONTACTOR_CONTROL); - ContactorControl cc = contactorControlChannel.value().asEnum(); - return cc == ContactorControl.ON_GRID; - } - - private boolean isSystemStopped() { - IntegerReadChannel contactorControlChannel = this.channel(VersionBChannelId.BMS_CONTACTOR_CONTROL); - ContactorControl cc = contactorControlChannel.value().asEnum(); - return cc == ContactorControl.CUT_OFF; - } - - private boolean isAlarmLevel2Error() { - return (readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CHA_CURRENT_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_LOW) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_DISCHA_CURRENT_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_LOW) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_SOC_LOW) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_TEMPERATURE_DIFFERENCE_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_POLES_TEMPERATURE_DIFFERENCE_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_DIFFERENCE_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_INSULATION_LOW) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_DIFFERENCE_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH) - || readValueFromBooleanChannel(VersionBChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW)); - } - - private boolean isSlaveCommunicationError() { - return readValueFromBooleanChannel(VersionBChannelId.SLAVE_20_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_19_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_18_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_17_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_16_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_15_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_14_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_13_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_12_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_11_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_10_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_9_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_8_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_7_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_6_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_5_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_4_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_3_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_2_COMMUNICATION_ERROR) - || readValueFromBooleanChannel(VersionBChannelId.SLAVE_1_COMMUNICATION_ERROR); - } - - private boolean isError() { - return isAlarmLevel2Error() || isSlaveCommunicationError(); - } - - private boolean readValueFromBooleanChannel(VersionBChannelId channelId) { - StateChannel r = this.channel(channelId); - Optional bOpt = r.value().asOptional(); - return bOpt.isPresent() && bOpt.get(); - } - - public String getModbusBridgeId() { - return modbusBridgeId; - } - - @Override - public String debugLog() { - return "SoC:" + this.getSoc().value() // - + "|Discharge:" + this.getDischargeMinVoltage().value() + ";" + this.getDischargeMaxCurrent().value() // - + "|Charge:" + this.getChargeMaxVoltage().value() + ";" + this.getChargeMaxCurrent().value() + "|State:" - + this.getStateMachineState(); - } - - private void startSystem() { - IntegerWriteChannel contactorControlChannel = this.channel(VersionBChannelId.BMS_CONTACTOR_CONTROL); - - Optional contactorControlOpt = contactorControlChannel.value().asOptional(); - // To avoid hardware damages do not send start command if system has already - // started - if (contactorControlOpt.isPresent()) { - int cc = contactorControlOpt.get(); - if (cc == ContactorControl.ON_GRID.getValue() || cc == ContactorControl.CONNECTION_INITIATING.getValue()) { - return; - } - } - - try { - log.debug("write value to contactor control channel: value: " + SYSTEM_ON); - contactorControlChannel.setNextWriteValue(SYSTEM_ON); - } catch (OpenemsException e) { - log.error("Error while trying to start system\n" + e.getMessage()); - } - } - - private void stopSystem() { - IntegerWriteChannel contactorControlChannel = this.channel(VersionBChannelId.BMS_CONTACTOR_CONTROL); - - Optional contactorControlOpt = contactorControlChannel.value().asOptional(); - // To avoid hardware damages do not send stop command if system has already - // stopped - if (contactorControlOpt.isPresent() && contactorControlOpt.get() == ContactorControl.CUT_OFF.getValue()) { - return; - } - - try { - log.debug("write value to contactor control channel: value: " + SYSTEM_OFF); - contactorControlChannel.setNextWriteValue(SYSTEM_OFF); - } catch (OpenemsException e) { - log.error("Error while trying to stop system\n" + e.getMessage()); - } - } - - public State getStateMachineState() { - return state; - } - - public void setStateMachineState(State state) { - this.state = state; - this.channel(VersionBChannelId.STATE_MACHINE).setNextValue(this.state); - } - - private void setSoCLowAlarm(int soCLowAlarm) { - try { - ((IntegerWriteChannel) this.channel(VersionBChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION)) - .setNextWriteValue(soCLowAlarm); - ((IntegerWriteChannel) this.channel(VersionBChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION_RECOVER)) - .setNextWriteValue(soCLowAlarm); - } catch (OpenemsException e) { - log.error("Error while setting parameter for soc low protection!" + e.getMessage()); - } - } - - @Override - protected ModbusProtocol defineModbusProtocol() { - - ModbusProtocol protocol = new ModbusProtocol(this, // - // Main switch - new FC6WriteRegisterTask(0x2010, - m(VersionBChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)) // - ), - - // System reset - new FC6WriteRegisterTask(0x2004, m(VersionBChannelId.SYSTEM_RESET, new UnsignedWordElement(0x2004)) // - ), - - // EMS timeout --> Watchdog - new FC6WriteRegisterTask(0x201C, - m(VersionBChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x201C)) // - ), - // Sleep - new FC6WriteRegisterTask(0x201D, m(VersionBChannelId.SLEEP, new UnsignedWordElement(0x201D)) // - ), - - // Work parameter - new FC6WriteRegisterTask(0x20C1, - m(VersionBChannelId.WORK_PARAMETER_PCS_COMMUNICATION_RATE, new UnsignedWordElement(0x20C1)) // - ), - - // Paramaeters for configuring - new FC6WriteRegisterTask(0x2014, - m(VersionBChannelId.AUTO_SET_SLAVES_ID, new UnsignedWordElement(0x2014))), - new FC6WriteRegisterTask(0x2019, - m(VersionBChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID, new UnsignedWordElement(0x2019))), - - // Control registers - new FC3ReadRegistersTask(0x2000, Priority.HIGH, // - m(VersionBChannelId.FAN_STATUS, new UnsignedWordElement(0x2000)), // - m(VersionBChannelId.MAIN_CONTACTOR_STATE, new UnsignedWordElement(0x2001)), // - m(VersionBChannelId.DRY_CONTACT_1_EXPORT, new UnsignedWordElement(0x2002)), // - m(VersionBChannelId.DRY_CONTACT_2_EXPORT, new UnsignedWordElement(0x2003)), // - m(VersionBChannelId.SYSTEM_RESET, new UnsignedWordElement(0x2004)), // - m(VersionBChannelId.SYSTEM_RUN_MODE, new UnsignedWordElement(0x2005)), // - m(VersionBChannelId.PRE_CONTACTOR_STATUS, new UnsignedWordElement(0x2006)), // - bm(new UnsignedWordElement(0x2007)) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_DISCHARGE_TEMPERATURE_LOW, 15) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_DISCHARGE_TEMPERATURE_HIGH, 14) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_VOLTAGE_DIFFERENCE, 13) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_INSULATION_LOW, 12) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_CELL_VOLTAGE_DIFFERENCE, 11) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_ELECTRODE_TEMPERATURE_HIGH, 10) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_TEMPERATURE_DIFFERENCE, 9) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_SOC_LOW, 8) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_CELL_OVER_TEMPERATURE, 7) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_CELL_LOW_TEMPERATURE, 6) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_DISCHARGE_OVER_CURRENT, 5) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_SYSTEM_LOW_VOLTAGE, 4) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_CELL_LOW_VOLTAGE, 3) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_CHARGE_OVER_CURRENT, 2) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_SYSTEM_OVER_VOLTAGE, 1) // - .m(VersionBChannelId.ALARM_FLAG_STATUS_CELL_OVER_VOLTAGE, 0) // - .build(), // - bm(new UnsignedWordElement(0x2008)) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_DISCHARGE_TEMPERATURE_LOW, 15) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_DISCHARGE_TEMPERATURE_HIGH, 14) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_VOLTAGE_DIFFERENCE, 13) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_INSULATION_LOW, 12) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_CELL_VOLTAGE_DIFFERENCE, 11) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_ELECTRODE_TEMPERATURE_HIGH, 10) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_TEMPERATURE_DIFFERENCE, 9) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_SOC_LOW, 8) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_CELL_OVER_TEMPERATURE, 7) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_CELL_LOW_TEMPERATURE, 6) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_DISCHARGE_OVER_CURRENT, 5) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_SYSTEM_LOW_VOLTAGE, 4) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_CELL_LOW_VOLTAGE, 3) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_CHARGE_OVER_CURRENT, 2) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_SYSTEM_OVER_VOLTAGE, 1) // - .m(VersionBChannelId.PROTECT_FLAG_STATUS_CELL_OVER_VOLTAGE, 0) // - .build(), // - m(VersionBChannelId.ALARM_FLAG_REGISTER_1, new UnsignedWordElement(0x2009)), // - m(VersionBChannelId.ALARM_FLAG_REGISTER_2, new UnsignedWordElement(0x200A)), // - m(VersionBChannelId.PROTECT_FLAG_REGISTER_1, new UnsignedWordElement(0x200B)), // - m(VersionBChannelId.PROTECT_FLAG_REGISTER_2, new UnsignedWordElement(0x200C)), // - m(VersionBChannelId.SHORT_CIRCUIT_FUNCTION, new UnsignedWordElement(0x200D)), // - m(VersionBChannelId.TESTING_IO, new UnsignedWordElement(0x200E)), // - m(VersionBChannelId.SOFT_SHUTDOWN, new UnsignedWordElement(0x200F)), // - m(VersionBChannelId.BMS_CONTACTOR_CONTROL, new UnsignedWordElement(0x2010)), // - m(VersionBChannelId.CURRENT_BOX_SELF_CALIBRATION, new UnsignedWordElement(0x2011)), // - m(VersionBChannelId.PCS_ALARM_RESET, new UnsignedWordElement(0x2012)), // - m(VersionBChannelId.INSULATION_SENSOR_FUNCTION, new UnsignedWordElement(0x2013)), // - m(VersionBChannelId.AUTO_SET_SLAVES_ID, new UnsignedWordElement(0x2014)), // - new DummyRegisterElement(0x2015, 0x2018), // - m(VersionBChannelId.AUTO_SET_SLAVES_TEMPERATURE_ID, new UnsignedWordElement(0x2019)), // - m(VersionBChannelId.TRANSPARENT_MASTER, new UnsignedWordElement(0x201A)), // - m(VersionBChannelId.SET_EMS_ADDRESS, new UnsignedWordElement(0x201B)), // - m(VersionBChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x201C)), // - m(VersionBChannelId.SLEEP, new UnsignedWordElement(0x201D)), // - m(VersionBChannelId.VOLTAGE_LOW_PROTECTION, new UnsignedWordElement(0x201E)) // - ), // - - // Voltage ranges - new FC3ReadRegistersTask(0x2082, Priority.LOW, // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2082), - ElementToChannelConverter.SCALE_FACTOR_2), // - new DummyRegisterElement(0x2083, 0x2087), - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2088), - ElementToChannelConverter.SCALE_FACTOR_2) // - ), - - // Summary state - new FC3ReadRegistersTask(0x2100, Priority.LOW, - m(VersionBChannelId.CLUSTER_1_VOLTAGE, new UnsignedWordElement(0x2100), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.CLUSTER_1_CURRENT, new UnsignedWordElement(0x2101), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.CLUSTER_1_CHARGE_INDICATION, new UnsignedWordElement(0x2102)), - m(Battery.ChannelId.SOC, new UnsignedWordElement(0x2103)), - m(VersionBChannelId.CLUSTER_1_SOH, new UnsignedWordElement(0x2104)), - m(VersionBChannelId.CLUSTER_1_MAX_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2105)), // - m(VersionBChannelId.CLUSTER_1_MAX_CELL_VOLTAGE, new UnsignedWordElement(0x2106)), // - m(VersionBChannelId.CLUSTER_1_MIN_CELL_VOLTAGE_ID, new UnsignedWordElement(0x2107)), // - m(VersionBChannelId.CLUSTER_1_MIN_CELL_VOLTAGE, new UnsignedWordElement(0x2108)), // - m(VersionBChannelId.CLUSTER_1_MAX_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x2109)), // - m(VersionBChannelId.CLUSTER_1_MAX_CELL_TEMPERATURE, new UnsignedWordElement(0x210A)), // - m(VersionBChannelId.CLUSTER_1_MIN_CELL_TEMPERATURE_ID, new UnsignedWordElement(0x210B)), // - m(VersionBChannelId.CLUSTER_1_MIN_CELL_TEMPERATURE, new UnsignedWordElement(0x210C)), // - m(VersionBChannelId.MAX_CELL_RESISTANCE_ID, new UnsignedWordElement(0x210D)), // - m(VersionBChannelId.MAX_CELL_RESISTANCE, new UnsignedWordElement(0x210E), - ElementToChannelConverter.SCALE_FACTOR_1), // - m(VersionBChannelId.MIN_CELL_RESISTANCE_ID, new UnsignedWordElement(0x210F)), // - m(VersionBChannelId.MIN_CELL_RESISTANCE, new UnsignedWordElement(0x2110), - ElementToChannelConverter.SCALE_FACTOR_1), // - m(VersionBChannelId.POSITIVE_INSULATION, new UnsignedWordElement(0x2111)), // - m(VersionBChannelId.NEGATIVE_INSULATION, new UnsignedWordElement(0x2112)), // - m(VersionBChannelId.MAIN_CONTACTOR_FLAG, new UnsignedWordElement(0x2113)), // - new DummyRegisterElement(0x2114), - m(VersionBChannelId.ENVIRONMENT_TEMPERATURE, new UnsignedWordElement(0x2115)), // - m(VersionBChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x2116)), // - m(VersionBChannelId.CELL_VOLTAGE_DIFFERENCE, new UnsignedWordElement(0x2117)), // - m(VersionBChannelId.TOTAL_VOLTAGE_DIFFERENCE, new UnsignedWordElement(0x2118), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.POWER_TEMPERATURE, new UnsignedWordElement(0x2119)), // - m(VersionBChannelId.POWER_SUPPLY_VOLTAGE, new UnsignedWordElement(0x211A)) // - ), - - // Critical state - new FC3ReadRegistersTask(0x2140, Priority.HIGH, // - bm(new UnsignedWordElement(0x2140)) // - .m(VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_HIGH, 0) // - .m(VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH, 1) // - .m(VersionBChannelId.ALARM_LEVEL_2_CHA_CURRENT_HIGH, 2) // - .m(VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_LOW, 3) // - .m(VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW, 4) // - .m(VersionBChannelId.ALARM_LEVEL_2_DISCHA_CURRENT_HIGH, 5) // - .m(VersionBChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH, 6) // - .m(VersionBChannelId.ALARM_LEVEL_2_CELL_CHA_TEMP_LOW, 7) // - .m(VersionBChannelId.ALARM_LEVEL_2_SOC_LOW, 8) // - .m(VersionBChannelId.ALARM_LEVEL_2_TEMPERATURE_DIFFERENCE_HIGH, 9) // - .m(VersionBChannelId.ALARM_LEVEL_2_POLES_TEMPERATURE_DIFFERENCE_HIGH, 10) // - .m(VersionBChannelId.ALARM_LEVEL_2_CELL_VOLTAGE_DIFFERENCE_HIGH, 11) // - .m(VersionBChannelId.ALARM_LEVEL_2_INSULATION_LOW, 12) // - .m(VersionBChannelId.ALARM_LEVEL_2_TOTAL_VOLTAGE_DIFFERENCE_HIGH, 13) // - .m(VersionBChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH, 14) // - .m(VersionBChannelId.ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - bm(new UnsignedWordElement(0x2141)) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_HIGH, 0) // - .m(VersionBChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH, 1) // - .m(VersionBChannelId.ALARM_LEVEL_1_CHA_CURRENT_HIGH, 2) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_LOW, 3) // - .m(VersionBChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW, 4) // - .m(VersionBChannelId.ALARM_LEVEL_1_DISCHA_CURRENT_HIGH, 5) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH, 6) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_CHA_TEMP_LOW, 7) // - .m(VersionBChannelId.ALARM_LEVEL_1_SOC_LOW, 8) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH, 9) // - .m(VersionBChannelId.ALARM_LEVEL_1_POLE_TEMPERATURE_TOO_HIGH, 10) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH, 11) // - .m(VersionBChannelId.ALARM_LEVEL_1_INSULATION_LOW, 12) // - .m(VersionBChannelId.ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH, 13) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH, 14) // - .m(VersionBChannelId.ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW, 15) // - .build(), // - m(VersionBChannelId.CLUSTER_RUN_STATE, new UnsignedWordElement(0x2142)), // - - m(VersionBChannelId.MAXIMUM_CELL_VOLTAGE_NUMBER_WHEN_ALARM, new UnsignedWordElement(0x2143)), // - m(VersionBChannelId.MAXIMUM_CELL_VOLTAGE_WHEN_ALARM, new UnsignedWordElement(0x2144)), // - m(VersionBChannelId.MAXIMUM_CELL_VOLTAGE_NUMBER_WHEN_STOPPED, new UnsignedWordElement(0x2145)), // - m(VersionBChannelId.MAXIMUM_CELL_VOLTAGE_WHEN_STOPPED, new UnsignedWordElement(0x2146)), // - m(VersionBChannelId.MINIMUM_CELL_VOLTAGE_NUMBER_WHEN_ALARM, new UnsignedWordElement(0x2147)), // - m(VersionBChannelId.MINIMUM_CELL_VOLTAGE_WHEN_ALARM, new UnsignedWordElement(0x2148)), // - m(VersionBChannelId.MINIMUM_CELL_VOLTAGE_NUMBER_WHEN_STOPPED, new UnsignedWordElement(0x2149)), // - m(VersionBChannelId.MINIMUM_CELL_VOLTAGE_WHEN_STOPPED, new UnsignedWordElement(0x214A)), // - m(VersionBChannelId.OVER_VOLTAGE_VALUE_WHEN_ALARM, new UnsignedWordElement(0x214B)), // - m(VersionBChannelId.OVER_VOLTAGE_VALUE_WHEN_STOPPED, new UnsignedWordElement(0x214C)), // - m(VersionBChannelId.UNDER_VOLTAGE_VALUE_WHEN_ALARM, new UnsignedWordElement(0x214D)), // - m(VersionBChannelId.UNDER_VOLTAGE_VALUE_WHEN_STOPPED, new UnsignedWordElement(0x214E)), // - m(VersionBChannelId.OVER_CHARGE_CURRENT_WHEN_ALARM, new UnsignedWordElement(0x214F)), // - m(VersionBChannelId.OVER_CHARGE_CURRENT_WHEN_STOPPED, new UnsignedWordElement(0x2150)), // - m(VersionBChannelId.OVER_DISCHARGE_CURRENT_WHEN_ALARM, new UnsignedWordElement(0x2151)), // - m(VersionBChannelId.OVER_DISCHARGE_CURRENT_WHEN_STOPPED, new UnsignedWordElement(0x2152)), // - m(VersionBChannelId.NUMBER_OF_TEMPERATURE_WHEN_ALARM, new UnsignedWordElement(0x2153)), // - new DummyRegisterElement(0x2154, 0x215A), // - m(VersionBChannelId.OTHER_ALARM_EQUIPMENT_FAILURE, new UnsignedWordElement(0x215B)), // - new DummyRegisterElement(0x215C, 0x215F), // - m(VersionBChannelId.SYSTEM_MAX_CHARGE_CURRENT, new UnsignedWordElement(0x2160), - ElementToChannelConverter.SCALE_FACTOR_2), // TODO Check if correct! - m(VersionBChannelId.SYSTEM_MAX_DISCHARGE_CURRENT, new UnsignedWordElement(0x2161), - ElementToChannelConverter.SCALE_FACTOR_2) // TODO Check if correct! - ), // - - // Cluster info - new FC3ReadRegistersTask(0x2180, Priority.LOW, // - m(VersionBChannelId.CYCLE_TIME, new UnsignedWordElement(0x2180)), // - m(VersionBChannelId.TOTAL_CAPACITY_HIGH_BITS, new UnsignedWordElement(0x2181)), // - m(VersionBChannelId.TOTAL_CAPACITY_LOW_BITS, new UnsignedWordElement(0x2182)), // - bm(new UnsignedWordElement(0x2183)) // - .m(VersionBChannelId.SLAVE_20_COMMUNICATION_ERROR, 3)// - .m(VersionBChannelId.SLAVE_19_COMMUNICATION_ERROR, 2)// - .m(VersionBChannelId.SLAVE_18_COMMUNICATION_ERROR, 1)// - .m(VersionBChannelId.SLAVE_17_COMMUNICATION_ERROR, 0)// - .build(), // - bm(new UnsignedWordElement(0x2184)) // - .m(VersionBChannelId.SLAVE_16_COMMUNICATION_ERROR, 15)// - .m(VersionBChannelId.SLAVE_15_COMMUNICATION_ERROR, 14)// - .m(VersionBChannelId.SLAVE_14_COMMUNICATION_ERROR, 13)// - .m(VersionBChannelId.SLAVE_13_COMMUNICATION_ERROR, 12)// - .m(VersionBChannelId.SLAVE_12_COMMUNICATION_ERROR, 11)// - .m(VersionBChannelId.SLAVE_11_COMMUNICATION_ERROR, 10)// - .m(VersionBChannelId.SLAVE_10_COMMUNICATION_ERROR, 9)// - .m(VersionBChannelId.SLAVE_9_COMMUNICATION_ERROR, 8)// - .m(VersionBChannelId.SLAVE_8_COMMUNICATION_ERROR, 7)// - .m(VersionBChannelId.SLAVE_7_COMMUNICATION_ERROR, 6)// - .m(VersionBChannelId.SLAVE_6_COMMUNICATION_ERROR, 5)// - .m(VersionBChannelId.SLAVE_5_COMMUNICATION_ERROR, 4)// - .m(VersionBChannelId.SLAVE_4_COMMUNICATION_ERROR, 3)// - .m(VersionBChannelId.SLAVE_3_COMMUNICATION_ERROR, 2)// - .m(VersionBChannelId.SLAVE_2_COMMUNICATION_ERROR, 1)// - .m(VersionBChannelId.SLAVE_1_COMMUNICATION_ERROR, 0)// - .build(), // - bm(new UnsignedWordElement(0x2185)) // - .m(VersionBChannelId.FAILURE_SAMPLING_WIRE, 0)// - .m(VersionBChannelId.FAILURE_CONNECTOR_WIRE, 1)// - .m(VersionBChannelId.FAILURE_LTC6803, 2)// - .m(VersionBChannelId.FAILURE_VOLTAGE_SAMPLING, 3)// - .m(VersionBChannelId.FAILURE_TEMP_SAMPLING, 4)// - .m(VersionBChannelId.FAILURE_TEMP_SENSOR, 5)// - .m(VersionBChannelId.FAILURE_GR_T, 6)// - .m(VersionBChannelId.FAILURE_PCB, 7)// - .m(VersionBChannelId.FAILURE_BALANCING_MODULE, 8)// - .m(VersionBChannelId.FAILURE_TEMP_SAMPLING_LINE, 9)// - .m(VersionBChannelId.FAILURE_INTRANET_COMMUNICATION, 10)// - .m(VersionBChannelId.FAILURE_EEPROM, 11)// - .m(VersionBChannelId.FAILURE_INITIALIZATION, 12)// - .build(), // - m(VersionBChannelId.SYSTEM_TIME_HIGH, new UnsignedWordElement(0x2186)), // - m(VersionBChannelId.SYSTEM_TIME_LOW, new UnsignedWordElement(0x2187)), // - new DummyRegisterElement(0x2188, 0x218E), // - m(VersionBChannelId.LAST_TIME_CHARGE_CAPACITY_LOW_BITS, new UnsignedWordElement(0x218F), - ElementToChannelConverter.SCALE_FACTOR_1), // - m(VersionBChannelId.LAST_TIME_CHARGE_END_TIME_HIGH_BITS, new UnsignedWordElement(0x2190)), // - m(VersionBChannelId.LAST_TIME_CHARGE_END_TIME_LOW_BITS, new UnsignedWordElement(0x2191)), // - new DummyRegisterElement(0x2192), // - m(VersionBChannelId.LAST_TIME_DISCHARGE_CAPACITY_LOW_BITS, new UnsignedWordElement(0x2193), - ElementToChannelConverter.SCALE_FACTOR_1), // - m(VersionBChannelId.LAST_TIME_DISCHARGE_END_TIME_HIGH_BITS, new UnsignedWordElement(0x2194)), // - m(VersionBChannelId.LAST_TIME_DISCHARGE_END_TIME_LOW_BITS, new UnsignedWordElement(0x2195)), // - m(VersionBChannelId.CELL_OVER_VOLTAGE_STOP_TIMES, new UnsignedWordElement(0x2196)), // - m(VersionBChannelId.BATTERY_OVER_VOLTAGE_STOP_TIMES, new UnsignedWordElement(0x2197)), // - m(VersionBChannelId.BATTERY_CHARGE_OVER_CURRENT_STOP_TIMES, new UnsignedWordElement(0x2198)), // - m(VersionBChannelId.CELL_VOLTAGE_LOW_STOP_TIMES, new UnsignedWordElement(0x2199)), // - m(VersionBChannelId.BATTERY_VOLTAGE_LOW_STOP_TIMES, new UnsignedWordElement(0x219A)), // - m(VersionBChannelId.BATTERY_DISCHARGE_OVER_CURRENT_STOP_TIMES, new UnsignedWordElement(0x219B)), // - m(VersionBChannelId.BATTERY_OVER_TEMPERATURE_STOP_TIMES, new UnsignedWordElement(0x219C)), // - m(VersionBChannelId.BATTERY_TEMPERATURE_LOW_STOP_TIMES, new UnsignedWordElement(0x219D)), // - m(VersionBChannelId.CELL_OVER_VOLTAGE_ALARM_TIMES, new UnsignedWordElement(0x219E)), // - m(VersionBChannelId.BATTERY_OVER_VOLTAGE_ALARM_TIMES, new UnsignedWordElement(0x219F)), // - m(VersionBChannelId.BATTERY_CHARGE_OVER_CURRENT_ALARM_TIMES, new UnsignedWordElement(0x21A0)), // - m(VersionBChannelId.CELL_VOLTAGE_LOW_ALARM_TIMES, new UnsignedWordElement(0x21A1)), // - m(VersionBChannelId.BATTERY_VOLTAGE_LOW_ALARM_TIMES, new UnsignedWordElement(0x21A2)), // - m(VersionBChannelId.BATTERY_DISCHARGE_OVER_CURRENT_ALARM_TIMES, - new UnsignedWordElement(0x21A3)), // - m(VersionBChannelId.BATTERY_OVER_TEMPERATURE_ALARM_TIMES, new UnsignedWordElement(0x21A4)), // - m(VersionBChannelId.BATTERY_TEMPERATURE_LOW_ALARM_TIMES, new UnsignedWordElement(0x21A5)), // - m(VersionBChannelId.SYSTEM_SHORT_CIRCUIT_PROTECTION_TIMES, new UnsignedWordElement(0x21A6)), // - m(VersionBChannelId.SYSTEM_GR_OVER_TEMPERATURE_STOP_TIMES, new UnsignedWordElement(0x21A7)), // - new DummyRegisterElement(0x21A8), // - m(VersionBChannelId.SYSTEM_GR_OVER_TEMPERATURE_ALARM_TIMES, new UnsignedWordElement(0x21A9)), // - new DummyRegisterElement(0x21AA), // - m(VersionBChannelId.BATTERY_VOLTAGE_DIFFERENCE_ALARM_TIMES, new UnsignedWordElement(0x21AB)), // - m(VersionBChannelId.BATTERY_VOLTAGE_DIFFERENCE_STOP_TIMES, new UnsignedWordElement(0x21AC)), // - new DummyRegisterElement(0x21AD, 0x21B3), // - m(VersionBChannelId.SLAVE_TEMPERATURE_COMMUNICATION_ERROR_HIGH, - new UnsignedWordElement(0x21B4)), // - m(VersionBChannelId.SLAVE_TEMPERATURE_COMMUNICATION_ERROR_LOW, new UnsignedWordElement(0x21B5)) // - ) // - ); // - - if (!config.ReduceTasks()) { - - // Add tasks to read/write work and warn parameters - // Stop parameter - Task writeStopParameters = new FC16WriteRegistersTask(0x2040, // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2040)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2041)), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2042), - ElementToChannelConverter.SCALE_FACTOR_2), // TODO - // Check if - // correct! - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2043), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_PROTECTION, - new UnsignedWordElement(0x2044), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x2045), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2046)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2048), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2049), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_PROTECTION, - new UnsignedWordElement(0x204A), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x204B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_PROTECTION, - new UnsignedWordElement(0x204C)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204D)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_PROTECTION, - new UnsignedWordElement(0x204E)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204F)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION, new UnsignedWordElement(0x2050)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION_RECOVER, new UnsignedWordElement(0x2051)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION, new UnsignedWordElement(0x2052)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION_RECOVER, new UnsignedWordElement(0x2053)), // - m(VersionBChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION, - new UnsignedWordElement(0x2054)), // - m(VersionBChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION_RECOVER, - new UnsignedWordElement(0x2055)), // - m(VersionBChannelId.STOP_PARAMETER_INSULATION_PROTECTION, new UnsignedWordElement(0x2056)), // - m(VersionBChannelId.STOP_PARAMETER_INSULATION_PROTECTION_RECOVER, new UnsignedWordElement(0x2057)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION, - new UnsignedWordElement(0x2058)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, - new UnsignedWordElement(0x2059)), // - m(VersionBChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION, - new UnsignedWordElement(0x205A), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, - new UnsignedWordElement(0x205B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION, - new UnsignedWordElement(0x205C)), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION_RECOVER, - new UnsignedWordElement(0x205D)), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION, - new UnsignedWordElement(0x205E)), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION_RECOVER, - new UnsignedWordElement(0x205F)), // - m(VersionBChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION, - new UnsignedWordElement(0x2060)), // - m(VersionBChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION_RECOVER, - new UnsignedWordElement(0x2061)) // - ); - -// //Warn parameter - Task writeWarnParameters = new FC16WriteRegistersTask(0x2080, // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2080)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2081)), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2082), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2083), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_ALARM, - new UnsignedWordElement(0x2084), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x2085), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2086)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2087)), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2088), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2089), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_ALARM, - new UnsignedWordElement(0x208A), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x208B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208C)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208D)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208E)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208F)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_LOW_ALARM, new UnsignedWordElement(0x2090)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_LOW_ALARM_RECOVER, new UnsignedWordElement(0x2091)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_HIGH_ALARM, new UnsignedWordElement(0x2092)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_HIGH_ALARM_RECOVER, new UnsignedWordElement(0x2093)), // - m(VersionBChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM, - new UnsignedWordElement(0x2094)), // - m(VersionBChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM_RECOVER, - new UnsignedWordElement(0x2095)), // - m(VersionBChannelId.WARN_PARAMETER_INSULATION_ALARM, new UnsignedWordElement(0x2096)), // - m(VersionBChannelId.WARN_PARAMETER_INSULATION_ALARM_RECOVER, new UnsignedWordElement(0x2097)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x2098)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, - new UnsignedWordElement(0x2099)), // - m(VersionBChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x209A), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, - new UnsignedWordElement(0x209B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM, - new UnsignedWordElement(0x209C)), // - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM_RECOVER, - new UnsignedWordElement(0x209D)), // - new DummyRegisterElement(0x209E), - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM, - new UnsignedWordElement(0x209F)), // - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM_RECOVER, - new UnsignedWordElement(0x20A0)), // - m(VersionBChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM, new UnsignedWordElement(0x20A1)), // - m(VersionBChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM_RECOVER, - new UnsignedWordElement(0x20A2)) // - ); - - // Stop parameter - Task readStopParameters = new FC3ReadRegistersTask(0x2040, Priority.LOW, // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2040)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2041)), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2042), - ElementToChannelConverter.SCALE_FACTOR_2), // TODO - // Check if - // correct! - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2043), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_PROTECTION, - new UnsignedWordElement(0x2044), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x2045), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2046)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2047)), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_PROTECTION, new UnsignedWordElement(0x2048), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2049), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_PROTECTION, - new UnsignedWordElement(0x204A), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x204B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_PROTECTION, - new UnsignedWordElement(0x204C)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204D)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_PROTECTION, - new UnsignedWordElement(0x204E)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x204F)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION, new UnsignedWordElement(0x2050)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_LOW_PROTECTION_RECOVER, new UnsignedWordElement(0x2051)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION, new UnsignedWordElement(0x2052)), // - m(VersionBChannelId.STOP_PARAMETER_SOC_HIGH_PROTECTION_RECOVER, new UnsignedWordElement(0x2053)), // - m(VersionBChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION, - new UnsignedWordElement(0x2054)), // - m(VersionBChannelId.STOP_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_PROTECTION_RECOVER, - new UnsignedWordElement(0x2055)), // - m(VersionBChannelId.STOP_PARAMETER_INSULATION_PROTECTION, new UnsignedWordElement(0x2056)), // - m(VersionBChannelId.STOP_PARAMETER_INSULATION_PROTECTION_RECOVER, new UnsignedWordElement(0x2057)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION, - new UnsignedWordElement(0x2058)), // - m(VersionBChannelId.STOP_PARAMETER_CELL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, - new UnsignedWordElement(0x2059)), // - m(VersionBChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION, - new UnsignedWordElement(0x205A), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_PROTECTION_RECOVER, - new UnsignedWordElement(0x205B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION, - new UnsignedWordElement(0x205C)), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_PROTECTION_RECOVER, - new UnsignedWordElement(0x205D)), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION, - new UnsignedWordElement(0x205E)), // - m(VersionBChannelId.STOP_PARAMETER_DISCHARGE_TEMPERATURE_LOW_PROTECTION_RECOVER, - new UnsignedWordElement(0x205F)), // - m(VersionBChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION, - new UnsignedWordElement(0x2060)), // - m(VersionBChannelId.STOP_PARAMETER_TEMPERATURE_DIFFERENCE_PROTECTION_RECOVER, - new UnsignedWordElement(0x2061)) // - ); - -// // Warn parameter - Task readWarnParameters = new FC3ReadRegistersTask(0x2080, Priority.LOW, // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_ALARM, new UnsignedWordElement(0x2080)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2081)), // - new DummyRegisterElement(0x2082), - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_OVER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2083), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_ALARM, - new UnsignedWordElement(0x2084), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_CHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x2085), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_ALARM, new UnsignedWordElement(0x2086)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2087)), // - new DummyRegisterElement(0x2088), - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_UNDER_VOLTAGE_RECOVER, new UnsignedWordElement(0x2089), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_ALARM, - new UnsignedWordElement(0x208A), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_SYSTEM_DISCHARGE_OVER_CURRENT_RECOVER, - new UnsignedWordElement(0x208B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208C)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_OVER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208D)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_ALARM, new UnsignedWordElement(0x208E)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_UNDER_TEMPERATURE_RECOVER, new UnsignedWordElement(0x208F)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_LOW_ALARM, new UnsignedWordElement(0x2090)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_LOW_ALARM_RECOVER, new UnsignedWordElement(0x2091)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_HIGH_ALARM, new UnsignedWordElement(0x2092)), // - m(VersionBChannelId.WARN_PARAMETER_SOC_HIGH_ALARM_RECOVER, new UnsignedWordElement(0x2093)), // - m(VersionBChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM, - new UnsignedWordElement(0x2094)), // - m(VersionBChannelId.WARN_PARAMETER_CONNECTOR_TEMPERATURE_HIGH_ALARM_RECOVER, - new UnsignedWordElement(0x2095)), // - m(VersionBChannelId.WARN_PARAMETER_INSULATION_ALARM, new UnsignedWordElement(0x2096)), // - m(VersionBChannelId.WARN_PARAMETER_INSULATION_ALARM_RECOVER, new UnsignedWordElement(0x2097)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x2098)), // - m(VersionBChannelId.WARN_PARAMETER_CELL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, - new UnsignedWordElement(0x2099)), // - m(VersionBChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM, new UnsignedWordElement(0x209A), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_TOTAL_VOLTAGE_DIFFERENCE_ALARM_RECOVER, - new UnsignedWordElement(0x209B), ElementToChannelConverter.SCALE_FACTOR_2), // - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM, - new UnsignedWordElement(0x209C)), // - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_HIGH_ALARM_RECOVER, - new UnsignedWordElement(0x209D)), // - new DummyRegisterElement(0x209E), - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM, - new UnsignedWordElement(0x209F)), // - m(VersionBChannelId.WARN_PARAMETER_DISCHARGE_TEMPERATURE_LOW_ALARM_RECOVER, - new UnsignedWordElement(0x20A0)), // - m(VersionBChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM, new UnsignedWordElement(0x20A1)), // - m(VersionBChannelId.WARN_PARAMETER_TEMPERATURE_DIFFERENCE_ALARM_RECOVER, - new UnsignedWordElement(0x20A2)) // - ); - - protocol.addTask(readStopParameters); - protocol.addTask(readWarnParameters); - protocol.addTask(writeStopParameters); - protocol.addTask(writeWarnParameters); - - // Add tasks for cell voltages and temperatures according to the number of - // slaves, one task per module is created - // Cell voltages - int offset = ModuleParameters.ADDRESS_OFFSET.getValue(); - int voltOffset = ModuleParameters.VOLTAGE_ADDRESS_OFFSET.getValue(); - int voltSensors = ModuleParameters.VOLTAGE_SENSORS_PER_MODULE.getValue(); - for (int i = 0; i < this.config.numberOfSlaves(); i++) { - Collection> elements = new ArrayList<>(); - for (int j = i * voltSensors; j < (i + 1) * voltSensors; j++) { - String key = getSingleCellPrefix(j) + KEY_VOLTAGE; - UnsignedWordElement uwe = new UnsignedWordElement(offset + voltOffset + j); - AbstractModbusElement ame = m(channelMap.get(key).channelId(), uwe); - elements.add(ame); - } - protocol.addTask(new FC3ReadRegistersTask(offset + voltOffset + i * voltSensors, Priority.LOW, - elements.toArray(new AbstractModbusElement[0]))); - } - - // Cell temperatures - int tempOffset = ModuleParameters.TEMPERATURE_ADDRESS_OFFSET.getValue(); - int tempSensors = ModuleParameters.TEMPERATURE_SENSORS_PER_MODULE.getValue(); - for (int i = 0; i < this.config.numberOfSlaves(); i++) { - Collection> elements = new ArrayList<>(); - for (int j = i * tempSensors; j < (i + 1) * tempSensors; j++) { - String key = getSingleCellPrefix(j) + KEY_TEMPERATURE; - SignedWordElement swe = new SignedWordElement(offset + tempOffset + j); - AbstractModbusElement ame = m(channelMap.get(key).channelId(), swe); - elements.add(ame); - } - protocol.addTask(new FC3ReadRegistersTask(offset + tempOffset + i * tempSensors, Priority.LOW, - elements.toArray(new AbstractModbusElement[0]))); - } - } - - return protocol; - } -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/State.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/State.java deleted file mode 100644 index 7fe7245fa0d..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/State.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.openems.edge.battery.soltaro.versionb; - -import io.openems.edge.common.channel.OptionsEnum; - -public enum State implements OptionsEnum { - UNDEFINED("Undefined", -1), // - OFF("Off", 1), // - INIT("Initializing", 2), // - RUNNING("Running", 3), // - STOPPING("Stopping", 4), // - ERROR("Error", 5), // - ERRORDELAY("Errordelay", 6); - - private State(String name, int value) { - this.name = name; - this.value = value; - } - - private int value; - private String name; - - @Override - public int getValue() { - return this.value; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public OptionsEnum getUndefined() { - return UNDEFINED; - } - -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/package-info.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/package-info.java deleted file mode 100644 index 144bc740cfb..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/versionb/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.edge.battery.soltaro.versionb; diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/AbstractModbusBridge.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/AbstractModbusBridge.java index c4f8348670d..4a154b08ecd 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/AbstractModbusBridge.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/AbstractModbusBridge.java @@ -35,6 +35,8 @@ public abstract class AbstractModbusBridge extends AbstractOpenemsComponent impl */ protected static final int DEFAULT_RETRIES = 1; + private LogVerbosity logVerbosity = LogVerbosity.NONE; + // private final Logger log = // LoggerFactory.getLogger(AbstractModbusBridge.class); private final ModbusWorker worker = new ModbusWorker(this); @@ -44,8 +46,10 @@ protected AbstractModbusBridge(io.openems.edge.common.channel.ChannelId[] firstI super(firstInitialChannelIds, furtherInitialChannelIds); } - protected void activate(ComponentContext context, String id, boolean enabled) { - super.activate(context, id, enabled); + protected void activate(ComponentContext context, String id, String alias, boolean enabled, + LogVerbosity logVerbosity) { + super.activate(context, id, alias, enabled); + this.logVerbosity = logVerbosity; if (this.isEnabled()) { this.worker.activate(id); } @@ -79,8 +83,11 @@ public void removeProtocol(String sourceId) { @Override public void handleEvent(Event event) { switch (event.getTopic()) { + case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE: + this.worker.onBeforeProcessImage(); + break; case EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE: - this.worker.triggerNextRun(); + this.worker.onExecuteWrite(); break; } } @@ -107,6 +114,15 @@ protected Channel getSlaveCommunicationFailedChannel() { return this.channel(BridgeModbus.ChannelId.SLAVE_COMMUNICATION_FAILED); } + public LogVerbosity getLogVerbosity() { + return logVerbosity; + } + + @Override + public void logInfo(Logger log, String message) { + super.logInfo(log, message); + } + @Override protected void logWarn(Logger log, String message) { super.logWarn(log, message); diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java index d25124c143b..e4b149c4d35 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java @@ -32,7 +32,10 @@ @Component(name = "Bridge.Modbus.Serial", // immediate = true, // configurationPolicy = ConfigurationPolicy.REQUIRE, // - property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE) + property = { // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE // + }) public class BridgeModbusSerialImpl extends AbstractModbusBridge implements BridgeModbus, BridgeModbusSerial, OpenemsComponent, EventHandler { @@ -66,6 +69,7 @@ public class BridgeModbusSerialImpl extends AbstractModbusBridge public enum ChannelId implements io.openems.edge.common.channel.ChannelId { ; + private final Doc doc; private ChannelId(Doc doc) { @@ -88,7 +92,7 @@ public BridgeModbusSerialImpl() { @Activate void activate(ComponentContext context, ConfigSerial config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled(), config.logVerbosity()); this.portName = config.portName(); this.baudrate = config.baudRate(); this.databits = config.databits(); diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusTcpImpl.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusTcpImpl.java index dbe1e7fdb65..ede60f3bb84 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusTcpImpl.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusTcpImpl.java @@ -32,7 +32,10 @@ @Component(name = "Bridge.Modbus.Tcp", // immediate = true, // configurationPolicy = ConfigurationPolicy.REQUIRE, // - property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE) + property = { // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE // + }) public class BridgeModbusTcpImpl extends AbstractModbusBridge implements BridgeModbus, BridgeModbusTcp, OpenemsComponent, EventHandler { @@ -68,7 +71,7 @@ public BridgeModbusTcpImpl() { @Activate protected void activate(ComponentContext context, ConfigTcp config) throws UnknownHostException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled(), config.logVerbosity()); this.setIpAddress(InetAddress.getByName(config.ip())); } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java index b2fc15d1610..d593f1d8059 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java @@ -10,8 +10,14 @@ name = "Bridge Modbus/RTU Serial", // description = "Provides a service for connecting to, querying and writing to a Modbus/RTU device.") @interface ConfigSerial { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "modbus0"; + @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 = "Port-Name", description = "The name of the serial port - e.g. '/dev/ttyUSB0' or 'COM3'") @@ -29,5 +35,8 @@ @AttributeDefinition(name = "Parity", description = "The parity - 'none', 'even', 'odd', 'mark' or 'space'") Parity parity() default Parity.NONE; + @AttributeDefinition(name = "Log-Verbosity", description = "The log verbosity.") + LogVerbosity logVerbosity() default LogVerbosity.NONE; + String webconsole_configurationFactory_nameHint() default "Bridge Modbus/RTU Serial [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigTcp.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigTcp.java index 4595d61371a..4f75b1cbddc 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigTcp.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigTcp.java @@ -7,12 +7,21 @@ name = "Bridge Modbus/TCP", // description = "Provides a service for connecting to, querying and writing to a Modbus/TCP device.") @interface ConfigTcp { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "modbus0"; + @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 = "IP-Address", description = "The IP address of the Modbus/TCP device.") String ip(); + @AttributeDefinition(name = "Log-Verbosity", description = "The log verbosity.") + LogVerbosity logVerbosity() default LogVerbosity.NONE; + String webconsole_configurationFactory_nameHint() default "Bridge Modbus/TCP [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/LogVerbosity.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/LogVerbosity.java new file mode 100644 index 00000000000..39307c47f1a --- /dev/null +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/LogVerbosity.java @@ -0,0 +1,16 @@ +package io.openems.edge.bridge.modbus; + +public enum LogVerbosity { + /** + * Show now logs. + */ + NONE, + /** + * Show logs for modbus write requests. + */ + WRITES, + /** + * Show verbose logs for modbus read and write requests. + */ + READS_AND_WRITES; +} diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ModbusWorker.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ModbusWorker.java index 6b6678f9fbf..bdd9b6f5a4a 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ModbusWorker.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ModbusWorker.java @@ -1,187 +1,242 @@ package io.openems.edge.bridge.modbus; +import java.util.ArrayList; import java.util.Collection; +import java.util.Deque; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.HashMultimap; +import com.google.common.base.Stopwatch; import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.worker.AbstractCycleWorker; +import io.openems.common.worker.AbstractImmediateWorker; +import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ModbusProtocol; import io.openems.edge.bridge.modbus.api.element.ModbusElement; import io.openems.edge.bridge.modbus.api.task.ReadTask; +import io.openems.edge.bridge.modbus.api.task.Task; +import io.openems.edge.bridge.modbus.api.task.WaitTask; import io.openems.edge.bridge.modbus.api.task.WriteTask; - -class ModbusWorker extends AbstractCycleWorker { +import io.openems.edge.common.channel.LongReadChannel; +import io.openems.edge.common.channel.StateChannel; +import io.openems.edge.common.taskmanager.MetaTasksManager; +import io.openems.edge.common.taskmanager.Priority; + +/** + * The ModbusWorker schedules the execution of all Modbus-Tasks, like reading + * and writing modbus registers. + * + *

    + * It tries to execute all Write-Tasks as early as possible (directly after the + * TOPIC_CYCLE_EXECUTE_WRITE event) and all Read-Tasks as late as possible to + * have correct values available exactly when they are needed (i.e. at the + * TOPIC_CYCLE_BEFORE_PROCESS_IMAGE event). + */ +class ModbusWorker extends AbstractImmediateWorker { + + private static final long TASK_DURATION_BUFFER = 50; private final Logger log = LoggerFactory.getLogger(ModbusWorker.class); + // Measures the Cycle-Length between two consecutive BeforeProcessImage events + private final Stopwatch cycleStopwatch = Stopwatch.createUnstarted(); + private final LinkedBlockingDeque tasksQueue = new LinkedBlockingDeque<>(); + private final MetaTasksManager readTasksManager = new MetaTasksManager<>(); + private final MetaTasksManager writeTasksManager = new MetaTasksManager<>(); + // Holds source Component-IDs that are known to have errors. + private final Set defectiveComponents = new HashSet<>(); + private final AbstractModbusBridge parent; - /** - * Holds the added protocols per source Component-ID. - */ - private final Multimap protocols = Multimaps - .synchronizedListMultimap(ArrayListMultimap.create()); + // The measured duration between BeforeProcessImage event and ExecuteWrite event + private long durationBetweenBeforeProcessImageTillExecuteWrite = 0; - /** - * Holds source Component-IDs that are known to have errors. - */ - private final Set errorComponents = new HashSet<>(); + protected ModbusWorker(AbstractModbusBridge parent) { + this.parent = parent; + } /** - * Set ForceWrite to interrupt the ReadTasks and execute the WriteTasks - * immediately. + * This is called on TOPIC_CYCLE_BEFORE_PROCESS_IMAGE cycle event. */ - private final AtomicBoolean forceWrite = new AtomicBoolean(false); + protected void onBeforeProcessImage() { + // Measure the actual cycle-time; and starts the next measure cycle + long cycleTime = 1000; // default to 1000 [ms] for the first run + if (this.cycleStopwatch.isRunning()) { + cycleTime = this.cycleStopwatch.elapsed(TimeUnit.MILLISECONDS); + } + this.cycleStopwatch.reset(); + this.cycleStopwatch.start(); - /** - * Counts the failed communications in a row. It is used so that - * SLAVE_COMMUNICATION_FAILED is not set on each single error. - */ - private final AtomicInteger communicationFailedCounter = new AtomicInteger(0); - private final static int FAILED_COMMUNICATIONS_FOR_ERROR = 5; + // If the current tasks queue spans multiple cycles and we are in-between -> + // stop here + if (!this.tasksQueue.isEmpty()) { + return; + } - private final AbstractModbusBridge parent; + // Collect the next read-tasks + List nextReadTasks = new ArrayList<>(); + ReadTask lowPriorityTask = this.getOneLowPriorityReadTask(); + if (lowPriorityTask != null) { + nextReadTasks.add(lowPriorityTask); + } + nextReadTasks.addAll(this.getAllHighPriorityReadTasks()); + long readTasksDuration = 0; + for (ReadTask task : nextReadTasks) { + readTasksDuration += task.getExecuteDuration(); + } - protected ModbusWorker(AbstractModbusBridge parent) { - this.parent = parent; + // collect the next write-tasks + long writeTasksDuration = 0; + List nextWriteTasks = this.getAllWriteTasks(); + for (WriteTask task : nextWriteTasks) { + writeTasksDuration += task.getExecuteDuration(); + } + + // plan the execution for the next cycles + long totalDuration = readTasksDuration + writeTasksDuration; + long totalDurationWithBuffer = totalDuration + TASK_DURATION_BUFFER; + long noOfRequiredCycles = ceilDiv(totalDurationWithBuffer, cycleTime); + + // Set EXECUTION_DURATION channel + LongReadChannel executionDurationChannel = this.parent.channel(BridgeModbus.ChannelId.EXECUTION_DURATION); + executionDurationChannel.setNextValue(totalDuration); + + // Set CYCLE_TIME_IS_TOO_SHORT state-channel + StateChannel cycleTimeIsTooShortChannel = this.parent.channel(BridgeModbus.ChannelId.CYCLE_TIME_IS_TOO_SHORT); + if (noOfRequiredCycles > 1) { + cycleTimeIsTooShortChannel.setNextValue(true); + } else { + cycleTimeIsTooShortChannel.setNextValue(false); + } + + long durationOfTasksBeforeExecuteWriteEvent = 0; + int noOfTasksBeforeExecuteWriteEvent = 0; + for (ReadTask task : nextReadTasks) { + if (durationOfTasksBeforeExecuteWriteEvent > this.durationBetweenBeforeProcessImageTillExecuteWrite) { + break; + } + noOfTasksBeforeExecuteWriteEvent++; + durationOfTasksBeforeExecuteWriteEvent += task.getExecuteDuration(); + } + + // Build Queue + Deque tasksQueue = new LinkedList<>(); + + // Add all write-tasks to the queue + tasksQueue.addAll(nextWriteTasks); + + // Add all read-tasks to the queue + for (int i = 0; i < nextReadTasks.size(); i++) { + ReadTask task = nextReadTasks.get(i); + if (i < noOfTasksBeforeExecuteWriteEvent) { + // this Task will be executed before ExecuteWrite event -> add it to the end of + // the queue + tasksQueue.addLast(task); + } else { + // this Task will be executed after ExecuteWrite event -> add it to the + // beginning of the queue + tasksQueue.addFirst(task); + } + } + + // Add a waiting-task to the end of the queue + long waitTillStart = noOfRequiredCycles * cycleTime - totalDurationWithBuffer; + tasksQueue.addLast(new WaitTask(waitTillStart)); + + // Copy all Tasks to the global tasks-queue + this.tasksQueue.clear(); + this.tasksQueue.addAll(tasksQueue); } - @Override - public void triggerNextRun() { - this.forceWrite.set(true); - super.triggerNextRun(); + /** + * This is called on TOPIC_CYCLE_EXECUTE_WRITE cycle event. + */ + public void onExecuteWrite() { + // calculate the duration between BeforeProcessImage event and ExecuteWrite + // event. This duration is used for planning the queue in onBeforeProcessImage() + if (this.cycleStopwatch.isRunning()) { + this.durationBetweenBeforeProcessImageTillExecuteWrite = this.cycleStopwatch.elapsed(TimeUnit.MILLISECONDS); + } else { + this.durationBetweenBeforeProcessImageTillExecuteWrite = 0; + } } @Override - protected void forever() { - boolean isCommunicationFailed = false; - - // get the read tasks for this run - Multimap nextReadTasks = this.getNextReadTasks(); - - /* - * execute next read tasks - */ - for (Entry> readEntry : nextReadTasks.asMap().entrySet()) { - /* - * was FORCE WRITE set? -> execute WriteTasks now - */ - if (this.forceWrite.getAndSet(false)) { - Multimap writeTasks = this.getNextWriteTasks(); - for (Entry> writeEntry : writeTasks.asMap().entrySet()) { - String componentId = writeEntry.getKey(); - for (WriteTask writeTask : writeEntry.getValue()) { - try { - // execute the task - writeTask.executeWrite(this.parent); - - // remove this component from erroneous list - this.errorComponents.remove(componentId); - - } catch (OpenemsException e) { - this.parent.logError(this.log, writeTask.toString() + " write failed: " + e.getMessage()); - - // mark this component as erroneous - this.errorComponents.add(componentId); - - // remember that at least one communication failed - isCommunicationFailed = true; - } - } + protected void forever() throws InterruptedException { + Task task = this.tasksQueue.takeLast(); + try { + // execute the task + int noOfExecutedSubTasks = task.execute(this.parent); + + if (noOfExecutedSubTasks > 0) { + // no exception & at least one sub-task executed -> remove this component from + // erroneous list and set the CommunicationFailedChannel to false + if (task.getParent() != null) { + this.defectiveComponents.remove(task.getParent().id()); } + + this.parent.getSlaveCommunicationFailedChannel().setNextValue(false); } - /* - * Execute next Read-Task - */ - { - String componentId = readEntry.getKey(); - for (ReadTask readTask : readEntry.getValue()) { - try { - // execute the task - readTask.executeQuery(this.parent); - - // remove this component from erroneous list - this.errorComponents.remove(componentId); - - } catch (OpenemsException e) { - this.parent.logWarn(this.log, readTask.toString() + " read failed: " + e.getMessage()); - - // mark this component as erroneous - this.errorComponents.add(componentId); - - // remember that at least one communication failed - isCommunicationFailed = true; - - // invalidate elements of this task - for (ModbusElement element : readTask.getElements()) { - element.invalidate(); - } - } - } + } catch (OpenemsException e) { + this.parent.logWarn(this.log, task.toString() + " execution failed: " + e.getMessage()); + + // mark this component as erroneous + if (task.getParent() != null) { + this.defectiveComponents.add(task.getParent().id()); } - } - /* - * did communication fail? - */ - if (isCommunicationFailed) { - if (this.communicationFailedCounter.incrementAndGet() > FAILED_COMMUNICATIONS_FOR_ERROR) { - // Set the "SLAVE_COMMUNICATION_FAILED" State-Channel - this.parent.getSlaveCommunicationFailedChannel().setNextValue(true); - } else { - // Unset the "SLAVE_COMMUNICATION_FAILED" State-Channel - this.parent.getSlaveCommunicationFailedChannel().setNextValue(false); + // set the CommunicationFailedChannel to true + this.parent.getSlaveCommunicationFailedChannel().setNextValue(true); + + // invalidate elements of this task + for (ModbusElement element : task.getElements()) { + element.invalidate(); } + } + } + + /** + * Gets one Read-Tasks with priority Low or Once. + * + * @return a list of ReadTasks by Source-ID + */ + private ReadTask getOneLowPriorityReadTask() { + // Get next Priority ONCE task + ReadTask oncePriorityTask = this.readTasksManager.getOneTask(Priority.ONCE); + if (oncePriorityTask != null && !oncePriorityTask.hasBeenExecuted()) { + return oncePriorityTask; + } else { - // Reset Counter - this.communicationFailedCounter.set(0); - // Unset the "SLAVE_COMMUNICATION_FAILED" State-Channel - this.parent.getSlaveCommunicationFailedChannel().setNextValue(false); + // No more Priority ONCE tasks available -> add Priority LOW task + ReadTask lowPriorityTask = this.readTasksManager.getOneTask(Priority.LOW); + if (lowPriorityTask != null) { + return lowPriorityTask; + } } + return null; } /** - * Gets the Read-Tasks by Source-ID. + * Gets all the High-Priority Read-Tasks. * *

    * This checks if a device is listed as defective and - if it is - adds only one * ReadTask of this Source-Component to the queue * - * @return a list of ReadTasks by Source-ID + * @return a list of ReadTasks */ - private Multimap getNextReadTasks() { - Multimap result = HashMultimap.create(); - for (Entry> entry : this.protocols.asMap().entrySet()) { - String componentId = entry.getKey(); - - for (ModbusProtocol protocol : entry.getValue()) { - if (this.errorComponents.contains(componentId)) { - // Component is known to be erroneous -> add only one Task - ReadTask t = protocol.getOneReadTask(); - if (t != null) { - result.put(componentId, t); - } - - } else { - // get the next read tasks from the protocol - List nextReadTasks = protocol.getNextReadTasks(); - result.putAll(entry.getKey(), nextReadTasks); - } - } - } - return result; + private List getAllHighPriorityReadTasks() { + Multimap tasks = this.readTasksManager.getAllTasksBySourceId(Priority.HIGH); + return this.filterDefectiveComponents(tasks); } /** @@ -193,24 +248,35 @@ private Multimap getNextReadTasks() { * * @return a list of WriteTasks by Source-ID */ - private Multimap getNextWriteTasks() { - Multimap result = HashMultimap.create(); - for (Entry> entry : this.protocols.asMap().entrySet()) { + private List getAllWriteTasks() { + Multimap tasks = this.writeTasksManager.getAllTasksBySourceId(); + return this.filterDefectiveComponents(tasks); + } + + /** + * Filters a Multimap with Tasks by Component-ID. For Components that are known + * to be defective, only one task is added; otherwise all tasks are added to the + * result. The idea is to not execute tasks that are known to fail. + * + * @param the Task type + * @param tasks Tasks by Componen-ID + * @return a list of filtered tasks + */ + private List filterDefectiveComponents(Multimap tasks) { + List result = new ArrayList<>(); + for (Entry> entry : tasks.asMap().entrySet()) { String componentId = entry.getKey(); - for (ModbusProtocol protocol : entry.getValue()) { - if (this.errorComponents.contains(componentId)) { - // Component is known to be erroneous -> add only one Task - WriteTask t = protocol.getOneWriteTask(); - if (t != null) { - result.put(componentId, t); - } - - } else { - // get the next read tasks from the protocol - List nextWriteTasks = protocol.getNextWriteTasks(); - result.putAll(entry.getKey(), nextWriteTasks); + if (this.defectiveComponents.contains(componentId)) { + // Component is known to be erroneous -> add only one Task + Iterator iterator = entry.getValue().iterator(); + if (iterator.hasNext()) { + result.add(iterator.next()); } + + } else { + // Component is ok. All all tasks. + result.addAll(entry.getValue()); } } return result; @@ -223,7 +289,8 @@ private Multimap getNextWriteTasks() { * @param protocol the ModbusProtocol */ public void addProtocol(String sourceId, ModbusProtocol protocol) { - this.protocols.put(sourceId, protocol); + this.readTasksManager.addTasksManager(sourceId, protocol.getReadTasksManager()); + this.writeTasksManager.addTasksManager(sourceId, protocol.getWriteTasksManager()); } /** @@ -232,6 +299,22 @@ public void addProtocol(String sourceId, ModbusProtocol protocol) { * @param sourceId Component-ID of the source */ public void removeProtocol(String sourceId) { - this.protocols.removeAll(sourceId); + this.readTasksManager.removeTasksManager(sourceId); + this.writeTasksManager.removeTasksManager(sourceId); + } + + /** + * This is a helper function. It calculates the opposite of Math.floorDiv(). + * + *

    + * Source: + * https://stackoverflow.com/questions/27643616/ceil-conterpart-for-math-floordiv-in-java + * + * @param x the dividend + * @param y the divisor + * @return the result of the division, rounded up + */ + private static long ceilDiv(long x, long y) { + return -Math.floorDiv(-x, y); } } \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java index 1781a10557f..175d5880eba 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java @@ -16,9 +16,9 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.OpenemsType; import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.ModbusCoilElement; import io.openems.edge.bridge.modbus.api.element.ModbusRegisterElement; -import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.WriteChannel; @@ -61,6 +61,7 @@ public abstract class AbstractOpenemsModbusComponent extends AbstractOpenemsComp * } * * + *

    * Note: the separation in firstInitialChannelIds and furtherInitialChannelIds * is only there to enforce that calling the constructor cannot be forgotten. * This way it needs to be called with at least one parameter - which is always @@ -88,6 +89,8 @@ protected void activate(String id) { * @param context ComponentContext of this component. Receive it from * parameter for @Activate * @param id ID of this component. Typically 'config.id()' + * @param alias Human-readable name of this Component. Typically + * 'config.alias()'. Defaults to 'id' if empty * @param enabled Whether the component should be enabled. Typically * 'config.enabled()' * @param unitId Unit-ID of the Modbus target @@ -99,9 +102,9 @@ protected void activate(String id) { * @param modbusId The ID of the Modbus brige. Typically * 'config.modbus_id()' */ - protected void activate(ComponentContext context, String id, boolean enabled, int unitId, ConfigurationAdmin cm, - String modbusReference, String modbusId) { - super.activate(context, id, enabled); + protected void activate(ComponentContext context, String id, String alias, boolean enabled, int unitId, + ConfigurationAdmin cm, String modbusReference, String modbusId) { + super.activate(context, id, alias, enabled); // update filter for 'Modbus' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "Modbus", modbusId)) { return; @@ -112,15 +115,23 @@ protected void activate(ComponentContext context, String id, boolean enabled, in modbus.addProtocol(this.id(), this.getModbusProtocol(this.unitId)); } } + + protected BridgeModbus getModbusBridge() { + return this.modbus.get(); + } @Override - protected void activate(ComponentContext context, String id, boolean enabled) { + protected void activate(ComponentContext context, String id, String alias, boolean enabled) { throw new IllegalArgumentException("Use the other activate() for Modbus compoenents!"); } @Override protected void deactivate() { super.deactivate(); + BridgeModbus modbus = this.modbus.getAndSet(null); + if (modbus != null) { + modbus.removeProtocol(this.id()); + } } public Integer getUnitId() { @@ -246,10 +257,20 @@ public AbstractModbusElement build() { * @param element the ModbusElement * @return a {@link ChannelMapper} */ - protected final ChannelMapper cm(AbstractModbusElement element) { + protected final ChannelMapper m(AbstractModbusElement element) { return new ChannelMapper(element); } + /** + * Maps the given BitsWordElement. + * + * @param bitsWordElement the ModbusElement + * @return the element parameter + */ + protected final AbstractModbusElement m(BitsWordElement bitsWordElement) { + return bitsWordElement; + } + /** * Maps the given element 1-to-1 to the Channel identified by channelId. * @@ -284,93 +305,6 @@ public enum BitConverter { DIRECT_1_TO_1, INVERT } - /** - * Private subclass to handle Channels that are mapping to one bit of a Modbus - * Unsigned Word element. - */ - public class BitChannelMapper { - private class ChannelWrapper { - private final Channel channel; - private final BitConverter converter; - - protected ChannelWrapper(Channel channel, BitConverter converter) { - this.channel = channel; - this.converter = converter; - } - } - - private final UnsignedWordElement element; - private final Map channels = new HashMap<>(); - - public BitChannelMapper(UnsignedWordElement element) { - this.element = element; - this.element.onUpdateCallback((value) -> { - if (value == null) { - return; - } - - this.channels.forEach((bitIndex, channelWrapper) -> { - if (bitIndex == null) { - log.warn("BitIndex is null for Channel [" + channelWrapper.channel.address() + "]"); - return; - } - - // Get value for this Channel - boolean setValue; - if (value << ~bitIndex < 0) { - setValue = true; - } else { - setValue = false; - } - - // Apply Bit-Conversion - BitConverter converter = channelWrapper.converter; - switch (converter) { - case DIRECT_1_TO_1: - break; - case INVERT: - setValue = !setValue; - break; - } - - // Set Value to Channel - Channel channel = channelWrapper.channel; - channel.setNextValue(setValue); - }); - }); - } - - public BitChannelMapper m(io.openems.edge.common.channel.ChannelId channelId, int bitIndex, - BitConverter converter) { - Channel channel = channel(channelId); - if (channel.getType() != OpenemsType.BOOLEAN) { - throw new IllegalArgumentException( - "Channel [" + channelId + "] must be of type [BOOLEAN] for bit-mapping."); - } - this.channels.put(bitIndex, new ChannelWrapper(channel, converter)); - return this; - } - - public BitChannelMapper m(io.openems.edge.common.channel.ChannelId channelId, int bitIndex) { - return m(channelId, bitIndex, BitConverter.DIRECT_1_TO_1); - } - - public UnsignedWordElement build() { - return this.element; - } - } - - /** - * Creates a BitChannelMapper that can be used with builder pattern inside the - * protocol definition.. - * - * @param element the ModbusElement - * @return the BitChannelMapper - */ - protected final BitChannelMapper bm(UnsignedWordElement element) { - return new BitChannelMapper(element); - } - /** * Converts upper/lower bytes to Short. * diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/BridgeModbus.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/BridgeModbus.java index f84861b2797..fdd5fac6f11 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/BridgeModbus.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/BridgeModbus.java @@ -2,15 +2,21 @@ import org.osgi.annotation.versioning.ProviderType; +import io.openems.common.channel.Debounce; +import io.openems.common.channel.Level; +import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; import io.openems.edge.common.component.OpenemsComponent; @ProviderType public interface BridgeModbus extends OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { - SLAVE_COMMUNICATION_FAILED(Doc.of(Level.FAULT)); + SLAVE_COMMUNICATION_FAILED(Doc.of(Level.FAULT) // + .debounce(10, Debounce.TRUE_VALUES_IN_A_ROW_TO_SET_TRUE)), // + CYCLE_TIME_IS_TOO_SHORT(Doc.of(Level.WARNING) // + .debounce(10, Debounce.TRUE_VALUES_IN_A_ROW_TO_SET_TRUE)), // + EXECUTION_DURATION(Doc.of(OpenemsType.LONG)); private final Doc doc; diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ElementToChannelConverter.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ElementToChannelConverter.java index 047f84e13a0..b12b51a42fc 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ElementToChannelConverter.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ElementToChannelConverter.java @@ -24,8 +24,8 @@ public class ElementToChannelConverter { * * @see ElementToChannelScaleFactorConverter */ - public final static ElementToChannelConverter SCALE_FACTOR_MINUS_1 = new ElementToChannelScaleFactorConverter(-1); - + public static final ElementToChannelConverter SCALE_FACTOR_MINUS_1 = new ElementToChannelScaleFactorConverter(-1); + /** * Applies a scale factor of -2. Converts value [1] to [0.01]. * @@ -145,8 +145,22 @@ public static ElementToChannelConverter INVERT_IF_TRUE(boolean invert) { * Applies {@link ElementToChannelConverter#SCALE_FACTOR_2_AND_KEEP_NEGATIVE} * and @see {@link ElementToChannelConverter#INVERT}. */ - public static final ElementToChannelConverter SCALE_FACTOR_2_AND_KEEP_NEGATIVE = new ElementToChannelConverterChain( + public static ElementToChannelConverter SCALE_FACTOR_2_AND_KEEP_NEGATIVE = new ElementToChannelConverterChain( SCALE_FACTOR_2_AND_KEEP_NEGATIVE_AND_INVERT, INVERT); + + /** + * Applies {@link ElementToChannelConverter#SCALE_FACTOR_1} and INVERT_IF_TRUE. + */ + public static final ElementToChannelConverter SCALE_FACTOR_1_AND_INVERT_IF_TRUE(boolean invert) { + return new ElementToChannelConverterChain(SCALE_FACTOR_1, INVERT_IF_TRUE(invert)); + } + + /** + * Applies {@link ElementToChannelConverter#SCALE_FACTOR_3} and INVERT_IF_TRUE. + */ + public static final ElementToChannelConverter SCALE_FACTOR_3_AND_INVERT_IF_TRUE(boolean invert) { + return new ElementToChannelConverterChain(SCALE_FACTOR_3, INVERT_IF_TRUE(invert)); + } private final Function elementToChannel; private final Function channelToElement; diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java index bbd12a047c8..ee4fb7e4a6e 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java @@ -49,7 +49,7 @@ public synchronized void addTasks(Task... tasks) { } /** - * Adds a Task to the Protocol + * Adds a Task to the Protocol. * * @param task the task */ @@ -82,39 +82,21 @@ public synchronized void removeTask(Task task) { } /** - * Returns the next list of WriteTasks that should be executed within one cycle. + * Gets the Read-Tasks Manager. * - * @return a list of WriteTasks + * @return a the TaskManager */ - public List getNextWriteTasks() { - return this.writeTaskManager.getNextTasks(); + public TasksManager getReadTasksManager() { + return this.readTaskManager; } /** - * Returns one WriteTask sequentially. + * Gets the Write-Tasks Manager. * - * @return a WriteTasks + * @return a the TaskManager */ - public WriteTask getOneWriteTask() { - return this.writeTaskManager.getOneTask(); - } - - /** - * Returns the next list of ReadTasks that should be executed within one cycle. - * - * @return a list of ReadTasks - */ - public List getNextReadTasks() { - return this.readTaskManager.getNextTasks(); - } - - /** - * Returns one ReadTask sequentially. - * - * @return a ReadTasks - */ - public ReadTask getOneReadTask() { - return this.readTaskManager.getOneTask(); + public TasksManager getWriteTasksManager() { + return this.writeTaskManager; } /** @@ -134,4 +116,16 @@ private synchronized void checkTask(Task task) { // TODO: check BitElements } } + + public void deactivate() { + List readTasks = this.readTaskManager.getAllTasks(); + for (ReadTask readTask : readTasks) { + readTask.deactivate(); + } + + List writeTasks = this.writeTaskManager.getAllTasks(); + for (WriteTask writeTask : writeTasks) { + writeTask.deactivate(); + } + } } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractDoubleWordElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractDoubleWordElement.java index 482369819bc..7626269734e 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractDoubleWordElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractDoubleWordElement.java @@ -84,6 +84,7 @@ public final void _setNextWriteValue(Optional valueOpt) throws OpenemsExcepti } else { this.setNextWriteValueRegisters(Optional.empty()); } + this.onSetNextWriteCallbacks.forEach(callback -> callback.accept(valueOpt)); } /** diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractModbusElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractModbusElement.java index 0b08a026f85..a889f3fe2c5 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractModbusElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractModbusElement.java @@ -1,6 +1,8 @@ package io.openems.edge.bridge.modbus.api.element; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; @@ -17,6 +19,8 @@ */ public abstract class AbstractModbusElement implements ModbusElement { + protected final List>> onSetNextWriteCallbacks = new ArrayList<>(); + private final Logger log = LoggerFactory.getLogger(AbstractModbusElement.class); private final OpenemsType type; @@ -35,6 +39,11 @@ public AbstractModbusElement(OpenemsType type, int startAddress, boolean isIgnor this.isIgnored = isIgnored; } + @Override + public final void onSetNextWrite(Consumer> callback) { + this.onSetNextWriteCallbacks.add(callback); + } + @Override public OpenemsType getType() { return this.type; @@ -108,4 +117,9 @@ protected boolean isDebug() { public String toString() { return this.startAddress + "/0x" + Integer.toHexString(this.startAddress); } + + @Override + public void deactivate() { + this.onUpdateCallbacks.clear(); + } } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractQuadrupleWordElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractQuadrupleWordElement.java index 153ab667851..db0cbb0c591 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractQuadrupleWordElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractQuadrupleWordElement.java @@ -89,6 +89,7 @@ public final void _setNextWriteValue(Optional valueOpt) throws OpenemsExcepti } else { this.setNextWriteValueRegisters(Optional.empty()); } + this.onSetNextWriteCallbacks.forEach(callback -> callback.accept(valueOpt)); } /** diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractWordElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractWordElement.java index cdb0a0e8cc5..3ea500df984 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractWordElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/AbstractWordElement.java @@ -33,7 +33,7 @@ public final int getLength() { } @Override - protected final void _setInputRegisters(InputRegister... registers) { + protected void _setInputRegisters(InputRegister... registers) { // convert registers ByteBuffer buff = ByteBuffer.allocate(2).order(getByteOrder()); buff.put(registers[0].toBytes()); @@ -51,7 +51,7 @@ protected final void _setInputRegisters(InputRegister... registers) { protected abstract T fromByteBuffer(ByteBuffer buff); @Override - public final void _setNextWriteValue(Optional valueOpt) throws OpenemsException { + public void _setNextWriteValue(Optional valueOpt) throws OpenemsException { if (this.isDebug()) { log.info("Element [" + this + "] set next write value to [" + valueOpt.orElse(null) + "]."); } @@ -64,6 +64,7 @@ public final void _setNextWriteValue(Optional valueOpt) throws OpenemsExcepti } else { this.setNextWriteValueRegisters(Optional.empty()); } + this.onSetNextWriteCallbacks.forEach(callback -> callback.accept(valueOpt)); } /** diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/BitsWordElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/BitsWordElement.java new file mode 100644 index 00000000000..6ac61209b6c --- /dev/null +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/BitsWordElement.java @@ -0,0 +1,259 @@ +package io.openems.edge.bridge.modbus.api.element; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.ghgande.j2mod.modbus.procimg.InputRegister; +import com.ghgande.j2mod.modbus.procimg.Register; +import com.ghgande.j2mod.modbus.procimg.SimpleRegister; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; +import io.openems.common.types.OpenemsType; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent.BitConverter; +import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.channel.ChannelId; +import io.openems.edge.common.channel.WriteChannel; + +/** + * A BitsWordElement is an {@link UnsignedWordElement} where every bit + * represents a Boolean value. + */ +public class BitsWordElement extends UnsignedWordElement { + + private final Logger log = LoggerFactory.getLogger(BitsWordElement.class); + + private final AbstractOpenemsModbusComponent component; + + /* + * Holds the ChannelWrapper. 'null' if not explicitely defined! + */ + private final ChannelWrapper[] channels = new ChannelWrapper[16]; + + public BitsWordElement(int address, AbstractOpenemsModbusComponent component) { + super(address); + this.component = component; + } + + @Override + protected BitsWordElement self() { + return this; + } + + /** + * Adds a mapping for a given bit. + * + * @param bitIndex the index of the bit; a number between 0 and 15 + * @param channelId the Channel-ID + * @param converter the Bit-Converter + * @return myself for builder pattern + */ + public BitsWordElement bit(int bitIndex, ChannelId channelId, BitConverter converter) { + if (bitIndex < 0 || bitIndex > 15) { + throw new IllegalArgumentException("Bit-Index must be between 0 and 15 for Channel-ID [" + channelId + "]"); + } + + Channel channel = this.component.channel(channelId); + if (channel.getType() != OpenemsType.BOOLEAN) { + throw new IllegalArgumentException("Channel [" + channelId + "] must be of type [BOOLEAN] for bit-mapping"); + } + @SuppressWarnings("unchecked") + Channel booleanChannel = (Channel) channel; + + // Handle Writes to Bit-Channels + ChannelWrapper channelWrapper = new ChannelWrapper(booleanChannel, converter); + if (channel instanceof WriteChannel) { + WriteChannel booleanWriteChannel = (WriteChannel) booleanChannel; + booleanWriteChannel.onSetNextWrite(value -> { + // Listen on Writes to the BooleanChannel and store the value + channelWrapper.setWriteValue(value); + }); + } + + this.channels[bitIndex] = channelWrapper; + return this; + } + + /** + * Adds a mapping for a given bit. + * + * @param bitIndex the index of the bit; a number between 0 and 15 + * @param channelId the Channel-ID + * @return myself for builder pattern + */ + public BitsWordElement bit(int bitIndex, ChannelId channelId) { + return this.bit(bitIndex, channelId, BitConverter.DIRECT_1_TO_1); + } + + /** + * Sets the individual BooleanChannel-Values from an InputRegister. + * + * @param registers the InputRegisters + */ + protected void _setInputRegisters(InputRegister... registers) { + if (registers.length != 1) { + throw new IllegalArgumentException("Expected only one Register instead of [" + registers.length + + "] for Component [" + this.component.id() + "] on address [" + this.getStartAddress() + "]"); + } + + // convert Register to int + ByteBuffer buff = ByteBuffer.allocate(2).order(this.getByteOrder()); + buff.put(registers[0].toBytes()); + int value = Short.toUnsignedInt(buff.getShort(0)); + + for (int bitIndex = 0; bitIndex < 16; bitIndex++) { + // Get Wrapper + ChannelWrapper wrapper = this.channels[bitIndex]; + if (wrapper == null) { + continue; + } + + // Get Value + boolean setValue = value << ~bitIndex < 0; + + // Apply Bit-Conversion + switch (wrapper.converter) { + case DIRECT_1_TO_1: + break; + case INVERT: + setValue = !setValue; + break; + } + + // Set Value to Channel + wrapper.channel.setNextValue(setValue); + } + } + + /** + * Gets the next write value from all Bits and resets them. + * + *

    + * This method should be called once in every cycle on the + * TOPIC_CYCLE_EXECUTE_WRITE event. It makes sure, that the nextWriteValue gets + * initialized in every Cycle. If registers need to be written again in every + * cycle, next setNextWriteValue()-method needs to called on every Cycle. + * + * @return the next value as an Optional array of Registers + */ + @Override + public Optional getNextWriteValueAndReset() { + // Check if any BooleanWriteChannel has a Write-Value; if none: return + // Optional.empty. + boolean isAnyBooleanWriteChannelSet = false; + for (ChannelWrapper wrapper : this.channels) { + if (wrapper != null && wrapper.getWriteValue().isPresent()) { + isAnyBooleanWriteChannelSet = true; + break; + } + } + if (!isAnyBooleanWriteChannelSet) { + return Optional.empty(); + } + + // Get value of each BooleanChannel + byte b0 = (byte) 0; + byte b1 = (byte) 0; + List channelsWithMissingWriteValue = new ArrayList<>(); + for (int bitIndex = 0; bitIndex < this.channels.length; bitIndex++) { + ChannelWrapper wrapper = this.channels[bitIndex]; + if (wrapper == null) { + continue; + } + Optional valueOpt = wrapper.getWriteValue(); + if (!valueOpt.isPresent()) { + channelsWithMissingWriteValue.add(wrapper.channel.address()); + continue; + } + // Write-Value exists + boolean value = valueOpt.get(); + if ((value && wrapper.converter == BitConverter.DIRECT_1_TO_1) // + || (!value && wrapper.converter == BitConverter.INVERT)) { + // Value is true -> set the bit of the byte + if (bitIndex < 8) { + b0 |= 1 << bitIndex; + } else { + b1 |= 1 << (bitIndex - 8); + } + } + } + + // If at least one BooleanWriteChannel had no Write-Value: Error + return + // Optional.empty. + if (!channelsWithMissingWriteValue.isEmpty()) { + new IllegalArgumentException( + "The following BooleanWriteChannels have no Write-Value: " + channelsWithMissingWriteValue.stream() // + .map(ChannelAddress::toString) // + .collect(Collectors.joining(","))).printStackTrace(); + return Optional.empty(); + } + + // Clear all Write-Values + for (ChannelWrapper wrapper : this.channels) { + if (wrapper != null) { + wrapper.setWriteValue(null); + } + } + + // create Register + Register result; + if (this.getByteOrder() == ByteOrder.BIG_ENDIAN) { + result = new SimpleRegister(b1, b0); + } else { + result = new SimpleRegister(b0, b1); + } + + // Log Debug + if (this.isDebug()) { + log.info("BitsWordElement [" + this + "]: next write value is to [" // + + String.format("%16s", Integer.toBinaryString(result.getValue())).replace(' ', '0') + // + "/0x" + String.format("%4s", Integer.toHexString(result.getValue())).replace(' ', '0') + "]."); + } + + return Optional.of(new Register[] { result }); + + } + + protected Integer fromByteBuffer(ByteBuffer buff) { + throw new IllegalArgumentException("BitsWordElement.fromByteBuffer() should never be called"); + } + + public Optional getNextWriteValue() { + throw new IllegalArgumentException("BitsWordElement.getNextWriteValue() should never be called"); + } + + public void _setNextWriteValue(Optional valueOpt) throws OpenemsException { + throw new IllegalArgumentException("BitsWordElement._setNextWriteValue() should never be called"); + } + + protected ByteBuffer toByteBuffer(ByteBuffer buff, Integer value) { + throw new IllegalArgumentException("BitsWordElement.toByteBuffer() should never be called"); + } + + private class ChannelWrapper { + private final Channel channel; + private final BitConverter converter; + private Optional writeValue = Optional.empty(); + + protected ChannelWrapper(Channel channel, BitConverter converter) { + this.channel = channel; + this.converter = converter; + } + + protected void setWriteValue(Boolean value) { + this.writeValue = Optional.ofNullable(value); + } + + protected Optional getWriteValue() { + return writeValue; + } + } +} diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/CoilElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/CoilElement.java index 2764c3d5811..b45651cd799 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/CoilElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/CoilElement.java @@ -1,6 +1,9 @@ package io.openems.edge.bridge.modbus.api.element; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,6 +17,7 @@ public class CoilElement extends AbstractModbusElement implements ModbusCoilElement { private final Logger log = LoggerFactory.getLogger(CoilElement.class); + private final List>> onSetNextWriteCallbacks = new ArrayList<>(); private Optional nextWriteValue = Optional.empty(); @@ -36,6 +40,7 @@ public void _setNextWriteValue(Optional valueOpt) throws OpenemsExcepti log.info("Element [" + this + "] set next write value to [" + valueOpt.orElse(null) + "]."); } this.nextWriteValue = valueOpt; + this.onSetNextWriteCallbacks.forEach(callback -> callback.accept(valueOpt)); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyCoilElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyCoilElement.java index e2c5a493941..e68e3311f2c 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyCoilElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyCoilElement.java @@ -21,7 +21,7 @@ public int getLength() { @Override public void _setNextWriteValue(Optional valueOpt) { // ignore write - return; + this.onSetNextWriteCallbacks.forEach(callback -> callback.accept(valueOpt)); } /** diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyRegisterElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyRegisterElement.java index 9797ed35854..1ba8ac52b7c 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyRegisterElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/DummyRegisterElement.java @@ -41,7 +41,7 @@ public void setInputRegisters(InputRegister... registers) { @Deprecated public void _setNextWriteValue(Optional valueOpt) { // ignore write - return; + this.onSetNextWriteCallbacks.forEach(callback -> callback.accept(valueOpt)); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/ModbusElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/ModbusElement.java index 4b1773700b5..f4e9ac054ff 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/ModbusElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/ModbusElement.java @@ -1,6 +1,7 @@ package io.openems.edge.bridge.modbus.api.element; import java.util.Optional; +import java.util.function.Consumer; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.OpenemsType; @@ -58,9 +59,23 @@ public interface ModbusElement { */ public void _setNextWriteValue(Optional valueOpt) throws OpenemsException; + /** + * Add an onSetNextWrite callback. It is called when a 'next write value' was + * set. + * + * @param callback the callback + */ + public void onSetNextWrite(Consumer> callback); + /** * Invalidates the Channel in case it could not be read from the Modbus device, * i.e. sets the value to 'UNDEFINED'/null. */ public void invalidate(); + + /** + * This is called on deactivate of the Modbus-Bridge. It can be used to clear + * any references like listeners. + */ + public void deactivate(); } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/SignedQuadruplewordElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/SignedQuadruplewordElement.java new file mode 100644 index 00000000000..56949696a91 --- /dev/null +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/SignedQuadruplewordElement.java @@ -0,0 +1,30 @@ +package io.openems.edge.bridge.modbus.api.element; + +import io.openems.common.types.OpenemsType; + +import java.nio.ByteBuffer; + +/** + * A SignedQuadruplewordElement represents a Long value in an + * {@link AbstractQuadrupleWordElement}. + */ +public class SignedQuadruplewordElement extends AbstractQuadrupleWordElement { + + public SignedQuadruplewordElement(int address) { + super(OpenemsType.LONG, address); + } + + @Override + protected SignedQuadruplewordElement self() { + return this; + } + + protected Long fromByteBuffer(ByteBuffer buff) { + return Long.valueOf(buff.getLong()); + } + + protected ByteBuffer toByteBuffer(ByteBuffer buff, Long value) { + return buff.putLong(value.longValue()); + } + +} diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/StringWordElement.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/StringWordElement.java index a7d2701ff82..2a34f8beff1 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/StringWordElement.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/element/StringWordElement.java @@ -69,6 +69,7 @@ public void _setNextWriteValue(Optional valueOpt) throws OpenemsExceptio } else { this.setNextWriteValueRegisters(Optional.empty()); } + this.onSetNextWriteCallbacks.forEach(callback -> callback.accept(valueOpt)); } protected String fromByteBuffer(ByteBuffer buff) { diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java index 2a396a06f52..8f4257711e9 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java @@ -1,11 +1,15 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.Arrays; +import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ghgande.j2mod.modbus.ModbusException; import com.ghgande.j2mod.modbus.msg.ModbusRequest; import com.ghgande.j2mod.modbus.msg.ModbusResponse; +import com.ghgande.j2mod.modbus.procimg.InputRegister; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.AbstractModbusBridge; @@ -17,8 +21,6 @@ * An abstract Modbus 'AbstractTask' is holding references to one or more Modbus * {@link AbstractModbusElement} which have register addresses in the same * range. - * - * @author stefan.feilmeier */ public abstract class AbstractReadTask extends AbstractTask implements ReadTask { @@ -31,13 +33,14 @@ public AbstractReadTask(int startAddress, Priority priority, AbstractModbusEleme this.priority = priority; } - public void executeQuery(AbstractModbusBridge bridge) throws OpenemsException { + public int _execute(AbstractModbusBridge bridge) throws OpenemsException { T[] response; try { /* * First try */ response = this.readElements(bridge); + } catch (OpenemsException | ModbusException e) { /* * Second try: with new connection @@ -45,6 +48,7 @@ public void executeQuery(AbstractModbusBridge bridge) throws OpenemsException { bridge.closeModbusConnection(); try { response = this.readElements(bridge); + } catch (ModbusException e2) { for (ModbusElement elem : this.getElements()) { if (!elem.isIgnored()) { @@ -61,30 +65,60 @@ public void executeQuery(AbstractModbusBridge bridge) throws OpenemsException { "Received message is too short. Expected [" + getLength() + "], got [" + response.length + "]"); } - fillElements(response); + this.fillElements(response); + return 1; } protected T[] readElements(AbstractModbusBridge bridge) throws OpenemsException, ModbusException { - ModbusRequest request = getRequest(); + ModbusRequest request = this.getRequest(); + int unitId = this.getParent().getUnitId(); ModbusResponse response = Utils.getResponse(request, this.getParent().getUnitId(), bridge); - return handleResponse(response); + + T[] result = this.handleResponse(response); + + // debug output + switch (this.getLogVerbosity(bridge)) { + case READS_AND_WRITES: + ; + + bridge.logInfo(this.log, this.getActiondescription() // + + " [" + unitId + ":" + this.getStartAddress() + "/0x" + Integer.toHexString(this.getStartAddress()) + + "]: " // + + Arrays.stream(result).map(r -> { + if (r instanceof InputRegister) { + return String.format("%4s", Integer.toHexString(((InputRegister) r).getValue())) + .replace(' ', '0'); + } else if (r instanceof Boolean) { + return ((Boolean) r) ? "x" : "-"; + } else { + return r.toString(); + } + }) // + .collect(Collectors.joining(" "))); + break; + case WRITES: + case NONE: + break; + } + + return result; } protected void fillElements(T[] response) { int position = 0; for (ModbusElement modbusElement : this.getElements()) { - if (!(isCorrectElementInstance(modbusElement))) { - doErrorLog(modbusElement); + if (!(this.isCorrectElementInstance(modbusElement))) { + this.doErrorLog(modbusElement); } else { try { if (!modbusElement.isIgnored()) { - doElementSetInput(modbusElement, position, response); + this.doElementSetInput(modbusElement, position, response); } } catch (OpenemsException e) { - doWarnLog(e); + this.doWarnLog(e); } } - position = increasePosition(position, modbusElement); + position = this.increasePosition(position, modbusElement); } } @@ -114,4 +148,5 @@ private void doErrorLog(ModbusElement modbusElement) { log.error("A " + getRequiredElementName() + " is required for a " + getActiondescription() + "Task! Element [" + modbusElement + "]"); } + } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java index 25867e2dd16..1e84121d1c1 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java @@ -1,5 +1,12 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.concurrent.TimeUnit; + +import com.google.common.base.Stopwatch; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.AbstractModbusBridge; +import io.openems.edge.bridge.modbus.LogVerbosity; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; import io.openems.edge.bridge.modbus.api.element.ModbusElement; @@ -8,16 +15,19 @@ * An abstract Modbus 'AbstractTask' is holding references to one or more Modbus * {@link AbstractModbusElement} which have register addresses in the same * range. - * - * @author stefan.feilmeier */ public abstract class AbstractTask implements Task { + private static final long DEFAULT_EXECUTION_DURATION = 300; + private final int length; private final int startAddress; + private final Stopwatch stopwatch = Stopwatch.createUnstarted(); private ModbusElement[] elements; private AbstractOpenemsModbusComponent parent = null; // this is always set by ModbusProtocol.addTask() + private boolean hasBeenExecutedSuccessfully = false; + private long lastExecuteDuration = DEFAULT_EXECUTION_DURATION; // initialize to some default public AbstractTask(int startAddress, AbstractModbusElement... elements) { this.startAddress = startAddress; @@ -48,14 +58,83 @@ public void setParent(AbstractOpenemsModbusComponent parent) { this.parent = parent; } + @Override public AbstractOpenemsModbusComponent getParent() { return parent; } + /** + * Executes the tasks - i.e. sends the query of a ReadTask or writes a + * WriteTask. + * + *

    + * Internally the _execute()-method of the specific subclass gets called. + * + * @param bridge the Modbus-Bridge + * @return the number of executed Sub-Tasks + * @throws OpenemsException on error + */ + public final synchronized int execute(AbstractModbusBridge bridge) throws OpenemsException { + this.stopwatch.reset(); + this.stopwatch.start(); + try { + int noOfSubTasksExecuted = this._execute(bridge); + + // no exception -> mark this task as successfully executed + this.hasBeenExecutedSuccessfully = true; + return noOfSubTasksExecuted; + + } finally { + this.lastExecuteDuration = this.stopwatch.elapsed(TimeUnit.MILLISECONDS); + } + } + + protected abstract int _execute(AbstractModbusBridge bridge) throws OpenemsException; + + /* + * Enable Debug mode for this Element. Activates verbose logging. TODO: + * implement debug write in all implementations (FC16 is already done) + */ + private boolean isDebug = false; + + public AbstractTask debug() { + this.isDebug = true; + return this; + } + + public boolean isDebug() { + return isDebug; + } + + /** + * Combines the global and local (via {@link #isDebug} log verbosity. + * + * @param bridge the parent Bridge + * @return the combined LogVerbosity + */ + protected LogVerbosity getLogVerbosity(AbstractModbusBridge bridge) { + if (this.isDebug) { + return LogVerbosity.READS_AND_WRITES; + } + return bridge.getLogVerbosity(); + } + + @Override + public boolean hasBeenExecuted() { + return this.hasBeenExecutedSuccessfully; + } + + @Override + public void deactivate() { + for (ModbusElement element : this.elements) { + element.deactivate(); + } + } + @Override public String toString() { StringBuffer sb = new StringBuffer(); - sb.append(getActiondescription()); + sb.append(this.getActiondescription()); sb.append(" ["); sb.append(this.parent.id()); sb.append(";unitid="); @@ -70,5 +149,9 @@ public String toString() { return sb.toString(); } + public long getExecuteDuration() { + return lastExecuteDuration; + } + protected abstract String getActiondescription(); } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java index d42c5425fdf..dc8b04a11f5 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java @@ -1,8 +1,10 @@ package io.openems.edge.bridge.modbus.api.task; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,33 +54,20 @@ public Register[] getRegisters() { public int getLastAddress() { return this.startAddress + registers.size() - 1; } - - @Override - public String toString() { - StringBuilder b = new StringBuilder( - "address [" + this.startAddress + "/0x" + Integer.toHexString(this.startAddress) + "] values ["); - for (int i = 0; i < this.registers.size(); i++) { - b.append(this.registers.get(i).getValue()); - if (i < this.registers.size() - 1) { - b.append(","); - } - } - b.append("]"); - return b.toString(); - } } @Override - public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { + public int _execute(AbstractModbusBridge bridge) throws OpenemsException { + int noOfWrittenRegisters = 0; List writes = mergeWriteRegisters(); // Execute combined writes for (CombinedWriteRegisters write : writes) { + Register[] registers = write.getRegisters(); try { /* * First try */ - this.writeMultipleRegisters(bridge, this.getParent().getUnitId(), write.startAddress, - write.getRegisters()); + this.writeMultipleRegisters(bridge, this.getParent().getUnitId(), write.startAddress, registers); } catch (OpenemsException | ModbusException e) { /* * Second try: with new connection @@ -91,15 +80,30 @@ public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { throw new OpenemsException("Transaction failed: " + e.getMessage(), e2); } } + noOfWrittenRegisters += registers.length; } + return noOfWrittenRegisters; } private void writeMultipleRegisters(AbstractModbusBridge bridge, int unitId, int startAddress, Register[] registers) throws ModbusException, OpenemsException { - WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(startAddress, registers); ModbusResponse response = Utils.getResponse(request, unitId, bridge); + // debug output + switch (this.getLogVerbosity(bridge)) { + case READS_AND_WRITES: + bridge.logInfo(this.log, "FC16WriteRegisters " // + + "[" + unitId + ":" + startAddress + "/0x" + Integer.toHexString(startAddress) + "]: " // + + Arrays.stream(registers) // + .map(r -> String.format("%4s", Integer.toHexString(r.getValue())).replace(' ', '0')) // + .collect(Collectors.joining(" "))); + break; + case WRITES: + case NONE: + break; + } + if (!(response instanceof WriteMultipleRegistersResponse)) { throw new OpenemsException("Unexpected Modbus response. Expected [WriteMultipleRegistersResponse], got [" + response.getClass().getSimpleName() + "]"); diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java index f65405d075f..0eea4c7b6b9 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java @@ -37,6 +37,6 @@ protected ModbusRequest getRequest() { @Override protected String getActiondescription() { - return "FC1 Read Coils"; + return "FC1ReadCoils"; } } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java index b35e0089479..39632001c8e 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java @@ -21,20 +21,20 @@ public FC2ReadInputsTask(int startAddress, Priority priority, AbstractModbusElem @Override protected BitVector getBitVector(ModbusResponse response) { - ReadInputDiscretesResponse readInputDiscretesResponse = (ReadInputDiscretesResponse) response; - return readInputDiscretesResponse.getDiscretes(); + ReadInputDiscretesResponse readInputDiscretesResponse = (ReadInputDiscretesResponse) response; + return readInputDiscretesResponse.getDiscretes(); } - + @Override - protected String getActiondescription() { - return "FC2 Read Coils"; + protected String getActiondescription() { + return "FC2ReadCoils"; } @Override protected String getExpectedInputClassname() { return "ReadInputDiscretesResponse"; } - + @Override protected ModbusRequest getRequest() { return new ReadInputDiscretesRequest(getStartAddress(), getLength()); diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java index 3ce1eb99ed0..128eced5fe2 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java @@ -11,8 +11,8 @@ import io.openems.edge.common.taskmanager.Priority; /** - * Implements a Read Holding Register abstractTask, implementing Modbus function code 3 - * (http://www.simplymodbus.ca/FC03.htm) + * Implements a Read Holding Register abstractTask, implementing Modbus function + * code 3 (http://www.simplymodbus.ca/FC03.htm) */ public class FC3ReadRegistersTask extends AbstractReadInputRegistersTask implements ReadTask { @@ -38,6 +38,6 @@ protected InputRegister[] handleResponse(ModbusResponse response) throws Openems @Override protected String getActiondescription() { - return "FC3 ReadHoldingRegister"; + return "FC3ReadHoldingRegisters"; } } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java index 68be165e425..abdc3c7548f 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java @@ -11,8 +11,8 @@ import io.openems.edge.common.taskmanager.Priority; /** - * Implements a Read Input Register abstractTask, implementing Modbus function code 4 - * (http://www.simplymodbus.ca/FC04.htm) + * Implements a Read Input Register abstractTask, implementing Modbus function + * code 4 (http://www.simplymodbus.ca/FC04.htm) */ public class FC4ReadInputRegistersTask extends AbstractReadInputRegistersTask implements ReadTask { @@ -22,14 +22,14 @@ public FC4ReadInputRegistersTask(int startAddress, Priority priority, AbstractMo @Override protected String getActiondescription() { - return "FC4 ReadHoldingRegister"; + return "FC4ReadInputRegisters"; } - + @Override protected ModbusRequest getRequest() { return new ReadInputRegistersRequest(getStartAddress(), getLength()); } - + @Override protected InputRegister[] handleResponse(ModbusResponse response) throws OpenemsException { if (response instanceof ReadInputRegistersResponse) { diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java index 89d5ea9fad8..2a81485ed7d 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java @@ -23,30 +23,34 @@ public class FC5WriteCoilTask extends AbstractTask implements WriteTask { private final Logger log = LoggerFactory.getLogger(FC5WriteCoilTask.class); - + public FC5WriteCoilTask(int startAddress, AbstractModbusElement element) { super(startAddress, element); } @Override - public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { + public int _execute(AbstractModbusBridge bridge) throws OpenemsException { + int noOfWrittenCoils = 0; ModbusElement element = this.getElements()[0]; if (element instanceof ModbusCoilElement) { Optional valueOpt = ((ModbusCoilElement) element).getNextWriteValueAndReset(); if (valueOpt.isPresent()) { // found value -> write + boolean value = valueOpt.get(); try { /* * First try */ - this.writeCoil(bridge, this.getParent().getUnitId(), this.getStartAddress(), valueOpt.get()); + this.writeCoil(bridge, this.getParent().getUnitId(), this.getStartAddress(), value); + noOfWrittenCoils = 1; } catch (OpenemsException | ModbusException e) { /* * Second try: with new connection */ bridge.closeModbusConnection(); try { - this.writeCoil(bridge, this.getParent().getUnitId(), this.getStartAddress(), valueOpt.get()); + this.writeCoil(bridge, this.getParent().getUnitId(), this.getStartAddress(), value); + noOfWrittenCoils = 1; } catch (ModbusException e2) { throw new OpenemsException("Transaction failed: " + e.getMessage(), e2); } @@ -55,14 +59,26 @@ public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { } else { log.warn("Unable to execute Write for ModbusElement [" + element + "]: No ModbusCoilElement!"); } + return noOfWrittenCoils; } private void writeCoil(AbstractModbusBridge bridge, int unitId, int startAddress, boolean value) throws OpenemsException, ModbusException { - WriteCoilRequest request = new WriteCoilRequest(startAddress, value); ModbusResponse response = Utils.getResponse(request, unitId, bridge); + // debug output + switch (this.getLogVerbosity(bridge)) { + case READS_AND_WRITES: + bridge.logInfo(this.log, "FC5WriteCoil " // + + "[" + unitId + ":" + startAddress + "/0x" + Integer.toHexString(startAddress) + "]: " // + + value); + break; + case WRITES: + case NONE: + break; + } + if (!(response instanceof WriteCoilResponse)) { throw new OpenemsException("Unexpected Modbus response. Expected [WriteCoilResponse], got [" + response.getClass().getSimpleName() + "]"); @@ -71,6 +87,6 @@ private void writeCoil(AbstractModbusBridge bridge, int unitId, int startAddress @Override protected String getActiondescription() { - return "FC5 Write Coil "; + return "FC5 WriteCoil"; } } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java index 5b3201e2c62..4d7d5c0410e 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java @@ -26,7 +26,8 @@ public FC6WriteRegisterTask(int startAddress, AbstractModbusElement element) } @Override - public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { + public int _execute(AbstractModbusBridge bridge) throws OpenemsException { + int noOfWrittenRegisters = 0; ModbusElement element = this.getElements()[0]; if (element instanceof AbstractWordElement) { @@ -37,13 +38,15 @@ public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { if (registers.length == 1 && registers[0] != null) { // found value -> write + Register register = registers[0]; try { /* * First try */ this.writeSingleRegister(bridge, this.getParent().getUnitId(), this.getStartAddress(), - registers[0]); + register); + noOfWrittenRegisters = 1; } catch (OpenemsException | ModbusException e) { /* * Second try: with new connection @@ -51,7 +54,8 @@ public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { bridge.closeModbusConnection(); try { this.writeSingleRegister(bridge, this.getParent().getUnitId(), this.getStartAddress(), - registers[0]); + register); + noOfWrittenRegisters = 1; } catch (ModbusException e2) { throw new OpenemsException("Transaction failed: " + e.getMessage(), e2); } @@ -63,19 +67,31 @@ public void executeWrite(AbstractModbusBridge bridge) throws OpenemsException { } else { log.warn("Unable to execute Write for ModbusElement [" + element + "]: No AbstractWordElement!"); } + return noOfWrittenRegisters; } @Override protected String getActiondescription() { - return "FC6 Write Register"; + return "FC6 WriteRegister"; } private void writeSingleRegister(AbstractModbusBridge bridge, int unitId, int startAddress, Register register) throws ModbusException, OpenemsException { - WriteSingleRegisterRequest request = new WriteSingleRegisterRequest(startAddress, register); ModbusResponse response = Utils.getResponse(request, unitId, bridge); + // debug output + switch (this.getLogVerbosity(bridge)) { + case READS_AND_WRITES: + case WRITES: + bridge.logInfo(this.log, "FC6WriteRegister " // + + "[" + unitId + ":" + startAddress + "/0x" + Integer.toHexString(startAddress) + "]: " // + + String.format("%4s", Integer.toHexString(register.getValue())).replace(' ', '0')); + break; + case NONE: + break; + } + if (!(response instanceof WriteSingleRegisterResponse)) { throw new OpenemsException("Unexpected Modbus response. Expected [WriteSingleRegisterResponse], got [" + response.getClass().getSimpleName() + "]"); diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/ReadTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/ReadTask.java index 3f24fec2ebc..e26d868f1e0 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/ReadTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/ReadTask.java @@ -1,7 +1,5 @@ package io.openems.edge.bridge.modbus.api.task; -import io.openems.common.exceptions.OpenemsException; -import io.openems.edge.bridge.modbus.AbstractModbusBridge; import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; import io.openems.edge.common.taskmanager.ManagedTask; @@ -10,17 +8,6 @@ * {@link AbstractModbusElement} which have register addresses in the same * range. The ReadTask handles the execution (query) on this range. @{link * WriteTask} inherits from ReadTask. - * - * @author stefan.feilmeier */ public interface ReadTask extends Task, ManagedTask { - - /** - * Sends a query for this AbstractTask to the Modbus device. - * - * @param bridge the Modbus-Bridge - * @param the Modbus-Element - * @throws OpenemsException on error - */ - public abstract void executeQuery(AbstractModbusBridge bridge) throws OpenemsException; } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java index 589fea70b01..c25bfbf3b39 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java @@ -1,5 +1,7 @@ package io.openems.edge.bridge.modbus.api.task; +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.AbstractModbusBridge; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.element.ModbusElement; import io.openems.edge.common.taskmanager.ManagedTask; @@ -27,4 +29,43 @@ public interface Task extends ManagedTask { */ void setParent(AbstractOpenemsModbusComponent parent); + /** + * Gets the parent. + * + * @return the parent + */ + AbstractOpenemsModbusComponent getParent(); + + /** + * This is called on deactivate of the Modbus-Bridge. It can be used to clear + * any references like listeners. + */ + void deactivate(); + + /** + * Executes the tasks - i.e. sends the query of a ReadTask or writes a + * WriteTask. + * + * @param bridge the Modbus-Bridge + * @param the Modbus-Element + * @throws OpenemsException on error + * @return the number of executed Sub-Tasks + */ + int execute(AbstractModbusBridge bridge) throws OpenemsException; + + /** + * Gets whether this ReadTask has been successfully executed before. + * + * @return true if this Task has been executed successfully at least once + */ + boolean hasBeenExecuted(); + + /** + * Gets the execution duration of the last execution (successful or not not + * successful) in [ms]. + * + * @return the duration in [ms] + */ + long getExecuteDuration(); + } \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/WaitTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/WaitTask.java new file mode 100644 index 00000000000..6371c86080c --- /dev/null +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/WaitTask.java @@ -0,0 +1,72 @@ +package io.openems.edge.bridge.modbus.api.task; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.AbstractModbusBridge; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.element.ModbusElement; +import io.openems.edge.common.taskmanager.Priority; + +public class WaitTask implements Task { + + private final Logger log = LoggerFactory.getLogger(WaitTask.class); + private final long delay; + + private AbstractOpenemsModbusComponent parent = null; + + public WaitTask(long delay) { + this.delay = delay; + } + + @Override + public Priority getPriority() { + return Priority.LOW; + } + + @Override + public ModbusElement[] getElements() { + return new ModbusElement[0]; + } + + @Override + public int getStartAddress() { + return 0; + } + + @Override + public void setParent(AbstractOpenemsModbusComponent parent) { + this.parent = parent; + } + + @Override + public AbstractOpenemsModbusComponent getParent() { + return this.parent; + } + + @Override + public void deactivate() { + } + + @Override + public int execute(AbstractModbusBridge bridge) throws OpenemsException { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + this.log.warn(e.getMessage()); + } + return 0; + } + + @Override + public boolean hasBeenExecuted() { + return true; + } + + @Override + public long getExecuteDuration() { + return this.delay; + } + +} \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/WriteTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/WriteTask.java index 5d8fa5f6d33..a898fb8059e 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/WriteTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/WriteTask.java @@ -1,20 +1,10 @@ package io.openems.edge.bridge.modbus.api.task; -import io.openems.common.exceptions.OpenemsException; -import io.openems.edge.bridge.modbus.AbstractModbusBridge; import io.openems.edge.common.taskmanager.ManagedTask; import io.openems.edge.common.taskmanager.Priority; public interface WriteTask extends Task, ManagedTask { - /** - * Executes writing for this WriteTask to the Modbus device. - * - * @param bridge the AbstractModbusBridge - * @throws OpenemsException on error - */ - public abstract void executeWrite(AbstractModbusBridge bridge) throws OpenemsException; - /** * Priority for WriteTasks is by default always HIGH. * diff --git a/io.openems.edge.common/.settings/org.eclipse.core.resources.prefs b/io.openems.edge.common/.settings/org.eclipse.core.resources.prefs index 7a9615f9e99..be4f61c90a7 100644 --- a/io.openems.edge.common/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.edge.common/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,4 @@ eclipse.preferences.version=1 -encoding//src/io/openems/edge/common/channel/Unit.java=UTF-8 encoding//src/io/openems/edge/common/channel/internal/package-info.java=UTF-8 encoding//src/io/openems/edge/common/component/package-info.java=UTF-8 encoding//src/io/openems/edge/common/event/package-info.java=UTF-8 diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/AccessMode.java b/io.openems.edge.common/src/io/openems/edge/common/channel/AccessMode.java deleted file mode 100644 index b43fada4743..00000000000 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/AccessMode.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.openems.edge.common.channel; - -public enum AccessMode { - - /** - * Read-Only - */ - READ_ONLY, - /** - * Read-Write - */ - READ_WRITE, - /** - * Write-Only - */ - WRITE_ONLY; - -} diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/BooleanWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/BooleanWriteChannel.java index 9f0ed37b39e..2c4ee54ebc9 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/BooleanWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/BooleanWriteChannel.java @@ -4,12 +4,18 @@ import java.util.Optional; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.CheckedConsumer; import io.openems.edge.common.component.OpenemsComponent; public class BooleanWriteChannel extends BooleanReadChannel implements WriteChannel { public static class MirrorToDebugChannel implements Consumer> { + private final Logger log = LoggerFactory.getLogger(MirrorToDebugChannel.class); + private final ChannelId targetChannelId; public MirrorToDebugChannel(ChannelId targetChannelId) { @@ -18,6 +24,12 @@ public MirrorToDebugChannel(ChannelId targetChannelId) { @Override public void accept(Channel channel) { + if (!(channel instanceof BooleanWriteChannel)) { + this.log.error("Channel [" + channel.address() + + "] is not an BooleanWriteChannel! Unable to register \"onSetNextWrite\"-Listener!"); + return; + } + // on each setNextWrite to the channel -> store the value in the DEBUG-channel ((BooleanWriteChannel) channel).onSetNextWrite(value -> { channel.getComponent().channel(this.targetChannelId).setNextValue(value); @@ -53,12 +65,12 @@ public Optional getNextWriteValue() { * @return */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java index 8e3fa826216..e27d9bba14e 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java @@ -1,6 +1,11 @@ package io.openems.edge.common.channel; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.ChannelCategory; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; +import io.openems.common.types.OptionsEnum; import io.openems.edge.common.channel.internal.AbstractDoc; import io.openems.edge.common.channel.internal.OpenemsTypeDoc; import io.openems.edge.common.component.OpenemsComponent; @@ -73,6 +78,13 @@ public static StateChannelDoc of(Level level) { return new StateChannelDoc(level); } + /** + * Gets the {@link ChannelCategory} of the Channel of this Doc. + * + * @return the ChannelCategory + */ + public ChannelCategory getChannelCategory(); + /** * Gets the OpenemsType. * diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/DoubleWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/DoubleWriteChannel.java index c955bbce146..52328653319 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/DoubleWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/DoubleWriteChannel.java @@ -2,8 +2,8 @@ import java.util.List; import java.util.Optional; -import java.util.function.Consumer; +import io.openems.common.exceptions.CheckedConsumer; import io.openems.edge.common.component.OpenemsComponent; public class DoubleWriteChannel extends DoubleReadChannel implements WriteChannel { @@ -34,12 +34,12 @@ public Optional getNextWriteValue() { * onSetNextWrite */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/EnumDoc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/EnumDoc.java index 95bd07f2547..b67f1a7c591 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/EnumDoc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/EnumDoc.java @@ -2,9 +2,11 @@ import java.util.Arrays; +import io.openems.common.channel.ChannelCategory; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.OpenemsType; +import io.openems.common.types.OptionsEnum; import io.openems.edge.common.channel.internal.AbstractDoc; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; @@ -18,11 +20,20 @@ public EnumDoc(OptionsEnum[] options) { this.options = options; } + @Override + public ChannelCategory getChannelCategory() { + return ChannelCategory.ENUM; + } + @Override protected EnumDoc self() { return this; } + public OptionsEnum[] getOptions() { + return options; + } + /** * Initial-Value. Default: none * @@ -62,7 +73,7 @@ public EnumReadChannel createChannelInstance(OpenemsComponent component, * * @return the Undefined-Option */ - private OptionsEnum getUndefinedOption() { + public OptionsEnum getUndefinedOption() { if (this.options.length == 0) { return null; } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/EnumReadChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/EnumReadChannel.java index 52c033f29ee..49748731391 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/EnumReadChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/EnumReadChannel.java @@ -2,6 +2,7 @@ import io.openems.common.types.OpenemsType; +import io.openems.common.types.OptionsEnum; import io.openems.edge.common.channel.internal.AbstractReadChannel; import io.openems.edge.common.component.OpenemsComponent; diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/EnumWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/EnumWriteChannel.java index 66ac95e715d..7851d8175e6 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/EnumWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/EnumWriteChannel.java @@ -4,7 +4,12 @@ import java.util.Optional; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.types.OptionsEnum; +import io.openems.common.exceptions.CheckedConsumer; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.common.component.OpenemsComponent; @@ -12,6 +17,8 @@ public class EnumWriteChannel extends EnumReadChannel implements WriteChannel> { + private final Logger log = LoggerFactory.getLogger(MirrorToDebugChannel.class); + private final ChannelId targetChannelId; public MirrorToDebugChannel(ChannelId targetChannelId) { @@ -20,6 +27,12 @@ public MirrorToDebugChannel(ChannelId targetChannelId) { @Override public void accept(Channel channel) { + if (!(channel instanceof EnumWriteChannel)) { + this.log.error("Channel [" + channel.address() + + "] is not an EnumWriteChannel! Unable to register \"onSetNextWrite\"-Listener!"); + return; + } + // on each setNextWrite to the channel -> store the value in the DEBUG-channel ((EnumWriteChannel) channel).onSetNextWrite(value -> { channel.getComponent().channel(this.targetChannelId).setNextValue(value); @@ -53,9 +66,9 @@ public void setNextWriteValue(String value) throws OpenemsNamedException { * Updates the 'next' write value of Channel from an Enum value. * * @param value the OptionsEnum value - * @throws OpenemsException on error + * @throws OpenemsNamedException on error */ - public void setNextWriteValue(OptionsEnum value) throws OpenemsException { + public void setNextWriteValue(OptionsEnum value) throws OpenemsNamedException { this.setNextWriteValue(value.getValue()); } @@ -79,12 +92,12 @@ public Optional getNextWriteValue() { * onSetNextWrite */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/FloatWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/FloatWriteChannel.java index 41b64427ff2..636026b912e 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/FloatWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/FloatWriteChannel.java @@ -4,12 +4,18 @@ import java.util.Optional; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.CheckedConsumer; import io.openems.edge.common.component.OpenemsComponent; public class FloatWriteChannel extends FloatReadChannel implements WriteChannel { public static class MirrorToDebugChannel implements Consumer> { + private final Logger log = LoggerFactory.getLogger(MirrorToDebugChannel.class); + private final ChannelId targetChannelId; public MirrorToDebugChannel(ChannelId targetChannelId) { @@ -18,6 +24,12 @@ public MirrorToDebugChannel(ChannelId targetChannelId) { @Override public void accept(Channel channel) { + if (!(channel instanceof FloatWriteChannel)) { + this.log.error("Channel [" + channel.address() + + "] is not an FloatWriteChannel! Unable to register \"onSetNextWrite\"-Listener!"); + return; + } + // on each setNextWrite to the channel -> store the value in the DEBUG-channel ((FloatWriteChannel) channel).onSetNextWrite(value -> { channel.getComponent().channel(this.targetChannelId).setNextValue(value); @@ -51,12 +63,12 @@ public Optional getNextWriteValue() { * onSetNextWrite */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/IntegerWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/IntegerWriteChannel.java index 2d5d1e0c713..b06f980472b 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/IntegerWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/IntegerWriteChannel.java @@ -4,13 +4,18 @@ import java.util.Optional; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.CheckedConsumer; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.channel.ChannelId; public class IntegerWriteChannel extends IntegerReadChannel implements WriteChannel { public static class MirrorToDebugChannel implements Consumer> { + private final Logger log = LoggerFactory.getLogger(MirrorToDebugChannel.class); + private final ChannelId targetChannelId; public MirrorToDebugChannel(ChannelId targetChannelId) { @@ -19,6 +24,12 @@ public MirrorToDebugChannel(ChannelId targetChannelId) { @Override public void accept(Channel channel) { + if (!(channel instanceof IntegerWriteChannel)) { + this.log.error("Channel [" + channel.address() + + "] is not an IntegerWriteChannel! Unable to register \"onSetNextWrite\"-Listener!"); + return; + } + // on each setNextWrite to the channel -> store the value in the DEBUG-channel ((IntegerWriteChannel) channel).onSetNextWrite(value -> { channel.getComponent().channel(this.targetChannelId).setNextValue(value); @@ -52,12 +63,12 @@ public Optional getNextWriteValue() { * onSetNextWrite */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/Level.java b/io.openems.edge.common/src/io/openems/edge/common/channel/Level.java deleted file mode 100644 index fddccd8fcd1..00000000000 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/Level.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.openems.edge.common.channel; - -/** - * Severity/visibility Level - */ -public enum Level implements OptionsEnum { - /** - * "OK" indicates, that everything is OK and there are no messages. - */ - OK(0, "Ok"), // - /** - * "Info" indicates, that everything is OK, but there is at least one - * informative messages available. - */ - INFO(1, "Info"), // - /** - * "Warning" indicates, that there is at least one warning message available. - */ - WARNING(2, "Warning"), // - /** - * "Fault" indicates, that there is at least one fault message available. - */ - FAULT(3, "Fault"); - - private final int value; - private final String name; - - private Level(int value, String name) { - this.value = value; - this.name = name; - } - - @Override - public int getValue() { - return value; - } - - @Override - public String getName() { - return name; - } - - @Override - public OptionsEnum getUndefined() { - return OK; - } -} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/LongWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/LongWriteChannel.java index dbdd613ff4b..6c19018bd66 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/LongWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/LongWriteChannel.java @@ -4,12 +4,18 @@ import java.util.Optional; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.CheckedConsumer; import io.openems.edge.common.component.OpenemsComponent; public class LongWriteChannel extends LongReadChannel implements WriteChannel { public static class MirrorToDebugChannel implements Consumer> { + private final Logger log = LoggerFactory.getLogger(MirrorToDebugChannel.class); + private final ChannelId targetChannelId; public MirrorToDebugChannel(ChannelId targetChannelId) { @@ -18,6 +24,12 @@ public MirrorToDebugChannel(ChannelId targetChannelId) { @Override public void accept(Channel channel) { + if (!(channel instanceof LongWriteChannel)) { + this.log.error("Channel [" + channel.address() + + "] is not an LongWriteChannel! Unable to register \"onSetNextWrite\"-Listener!"); + return; + } + // on each setNextWrite to the channel -> store the value in the DEBUG-channel ((LongWriteChannel) channel).onSetNextWrite(value -> { channel.getComponent().channel(this.targetChannelId).setNextValue(value); @@ -51,12 +63,12 @@ public Optional getNextWriteValue() { * onSetNextWrite */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/ShortWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/ShortWriteChannel.java index d4fddd87593..512504e57cb 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/ShortWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/ShortWriteChannel.java @@ -4,12 +4,18 @@ import java.util.Optional; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.CheckedConsumer; import io.openems.edge.common.component.OpenemsComponent; public class ShortWriteChannel extends ShortReadChannel implements WriteChannel { public static class MirrorToDebugChannel implements Consumer> { + private final Logger log = LoggerFactory.getLogger(MirrorToDebugChannel.class); + private final ChannelId targetChannelId; public MirrorToDebugChannel(ChannelId targetChannelId) { @@ -18,6 +24,12 @@ public MirrorToDebugChannel(ChannelId targetChannelId) { @Override public void accept(Channel channel) { + if (!(channel instanceof ShortWriteChannel)) { + this.log.error("Channel [" + channel.address() + + "] is not an ShortWriteChannel! Unable to register \"onSetNextWrite\"-Listener!"); + return; + } + // on each setNextWrite to the channel -> store the value in the DEBUG-channel ((ShortWriteChannel) channel).onSetNextWrite(value -> { channel.getComponent().channel(this.targetChannelId).setNextValue(value); @@ -51,12 +63,12 @@ public Optional getNextWriteValue() { * onSetNextWrite */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannel.java index f860657877d..233c12e32e7 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannel.java @@ -1,5 +1,9 @@ package io.openems.edge.common.channel; +import java.util.Optional; + +import io.openems.common.channel.Debounce; +import io.openems.common.channel.Level; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.internal.AbstractDoc; import io.openems.edge.common.channel.internal.AbstractReadChannel; @@ -13,11 +17,17 @@ public class StateChannel extends AbstractReadChannel, Boolean> { private final Level level; + private final int debounce; + private Debounce debounceMode = null; + + private int debounceCounter = 0; protected StateChannel(OpenemsComponent component, ChannelId channelId, AbstractDoc channelDoc, - Level level) { + Level level, int debounce, Debounce debounceMode) { super(OpenemsType.BOOLEAN, component, channelId, channelDoc, false); this.level = level; + this.debounce = debounce; + this.debounceMode = debounceMode; } /** @@ -29,4 +39,71 @@ public Level getLevel() { return level; } + @SuppressWarnings("deprecation") + @Override + public void _setNextValue(Boolean value) { + // this can happen once in the beginning when called via super constructor + if (this.debounceMode == null) { + super._setNextValue(value); + return; + } + + switch (this.debounceMode) { + case TRUE_VALUES_IN_A_ROW_TO_SET_TRUE: + this.trueValuesInARowToSetTrue(value); + break; + + case FALSE_VALUES_IN_A_ROW_TO_SET_FALSE: + this.falseValuesInARowToSetFalse(value); + break; + + case SAME_VALUES_IN_A_ROW_TO_CHANGE: + Optional currentValueOpt = this.value().asOptional(); + if (!currentValueOpt.isPresent()) { + super._setNextValue(value); + return; + } + boolean currentValue = currentValueOpt.get(); + if (currentValue) { + this.falseValuesInARowToSetFalse(value); + } else { + this.trueValuesInARowToSetTrue(value); + } + break; + } + } + + @SuppressWarnings("deprecation") + private void trueValuesInARowToSetTrue(Boolean value) { + if (value != null && value) { + if (this.debounceCounter <= this.debounce) { + this.debounceCounter++; + } + } else { + this.debounceCounter = 0; + } + + if (this.debounceCounter > this.debounce) { + super._setNextValue(true); + } else { + super._setNextValue(false); + } + } + + @SuppressWarnings("deprecation") + private void falseValuesInARowToSetFalse(Boolean value) { + if (value != null && !value) { + if (this.debounceCounter <= this.debounce) { + this.debounceCounter++; + } + } else { + this.debounceCounter = 0; + } + + if (this.debounceCounter > this.debounce) { + super._setNextValue(false); + } else { + super._setNextValue(true); + } + } } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannelDoc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannelDoc.java index fc3641d0d57..a3f8528906d 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannelDoc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/StateChannelDoc.java @@ -1,5 +1,8 @@ package io.openems.edge.common.channel; +import io.openems.common.channel.ChannelCategory; +import io.openems.common.channel.Debounce; +import io.openems.common.channel.Level; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.internal.AbstractDoc; import io.openems.edge.common.component.OpenemsComponent; @@ -13,11 +16,20 @@ public StateChannelDoc(Level level) { this.level = level; } + @Override + public ChannelCategory getChannelCategory() { + return ChannelCategory.STATE; + } + @Override protected StateChannelDoc self() { return this; } + public Level getLevel() { + return level; + } + /** * Creates an instance of {@link Channel} for the given Channel-ID using its * Channel-{@link Doc}. @@ -29,6 +41,27 @@ protected StateChannelDoc self() { @Override public StateChannel createChannelInstance(OpenemsComponent component, io.openems.edge.common.channel.ChannelId channelId) { - return new StateChannel(component, channelId, this, this.level); + return new StateChannel(component, channelId, this, this.level, this.debounce, this.debounceMode); + } + + /** + * Debounce the State-Channel value: the StateChannel is only set to true after + * it had been set to true for at least "debounce" times. + */ + private int debounce = 0; + private Debounce debounceMode = Debounce.TRUE_VALUES_IN_A_ROW_TO_SET_TRUE; + + public StateChannelDoc debounce(int debounce, Debounce debounceMode) { + this.debounce = debounce; + this.debounceMode = debounceMode; + return this; + } + + public int getDebounce() { + return debounce; + } + + public Debounce getDebounceMode() { + return debounceMode; } } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/StringWriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/StringWriteChannel.java index ea486107059..7a169ceefb6 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/StringWriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/StringWriteChannel.java @@ -4,12 +4,18 @@ import java.util.Optional; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.CheckedConsumer; import io.openems.edge.common.component.OpenemsComponent; public class StringWriteChannel extends StringReadChannel implements WriteChannel { public static class MirrorToDebugChannel implements Consumer> { + private final Logger log = LoggerFactory.getLogger(MirrorToDebugChannel.class); + private final ChannelId targetChannelId; public MirrorToDebugChannel(ChannelId targetChannelId) { @@ -18,6 +24,12 @@ public MirrorToDebugChannel(ChannelId targetChannelId) { @Override public void accept(Channel channel) { + if (!(channel instanceof StringWriteChannel)) { + this.log.error("Channel [" + channel.address() + + "] is not an StringWriteChannel! Unable to register \"onSetNextWrite\"-Listener!"); + return; + } + // on each setNextWrite to the channel -> store the value in the DEBUG-channel ((StringWriteChannel) channel).onSetNextWrite(value -> { channel.getComponent().channel(this.targetChannelId).setNextValue(value); @@ -51,12 +63,12 @@ public Optional getNextWriteValue() { * onSetNextWrite */ @Override - public List> getOnSetNextWrites() { + public List> getOnSetNextWrites() { return super.getOnSetNextWrites(); } @Override - public void onSetNextWrite(Consumer callback) { + public void onSetNextWrite(CheckedConsumer callback) { this.getOnSetNextWrites().add(callback); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/WriteChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/WriteChannel.java index 0629f040cea..b228dbd7007 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/WriteChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/WriteChannel.java @@ -2,9 +2,9 @@ import java.util.List; import java.util.Optional; -import java.util.function.Consumer; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.CheckedConsumer; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.type.TypeUtils; public interface WriteChannel extends Channel { @@ -13,8 +13,9 @@ public interface WriteChannel extends Channel { * Updates the 'next' write value of Channel. * * @param value the typed value + * @throws OpenemsNamedException on error */ - public default void setNextWriteValue(T value) throws OpenemsException { + public default void setNextWriteValue(T value) throws OpenemsNamedException { this.setNextWriteValueFromObject(value); } @@ -26,12 +27,23 @@ public default void setNextWriteValue(T value) throws OpenemsException { * {@link WriteChannel#setNextWriteValue(Object)} directly. * * @param value the value as an Object + * @throws OpenemsNamedException on error */ - public default void setNextWriteValueFromObject(Object value) throws OpenemsException { + public default void setNextWriteValueFromObject(Object value) throws OpenemsNamedException { T typedValue = TypeUtils.getAsType(this.getType(), value); + OpenemsNamedException exception = null; // set the write value this._setNextWriteValue(typedValue); - this.getOnSetNextWrites().forEach(callback -> callback.accept(typedValue)); + for (CheckedConsumer callback : this.getOnSetNextWrites()) { + try { + callback.accept(typedValue); + } catch (OpenemsNamedException e) { + exception = e; + } + } + if (exception != null) { + throw exception; + } } /** @@ -65,9 +77,12 @@ public default Optional getNextWriteValueAndReset() { /** * Add an onSetNextWrite callback. It is called when a 'next write value' was * set. + * + *

    + * The callback can throw an {@link OpenemsNamedException}. */ - public void onSetNextWrite(Consumer callback); + public void onSetNextWrite(CheckedConsumer callback); - public List> getOnSetNextWrites(); + public List> getOnSetNextWrites(); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java index 927dff2af45..0e0664d33ab 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java @@ -4,11 +4,11 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; /** diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java index a82746477fe..07e9913671e 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java @@ -8,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.common.exceptions.CheckedConsumer; import io.openems.common.types.ChannelAddress; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; @@ -118,7 +119,7 @@ public OpenemsType getType() { * @param value the next value */ @Deprecated - public final void _setNextValue(T value) { + public void _setNextValue(T value) { this.nextValue = new Value(this, value); if (this.channelDoc.isDebug()) { log.info("Next value for [" + this.address() + "]: " + this.nextValue.asString()); @@ -161,9 +162,9 @@ public void onChange(Consumer> callback) { * 'onSetNextWriteCallbacks' is not final by purpose, because it might be called * in construction and would not be initialised then. */ - private List> onSetNextWriteCallbacks = null; + private List> onSetNextWriteCallbacks = null; - protected List> getOnSetNextWrites() { + protected List> getOnSetNextWrites() { if (this.onSetNextWriteCallbacks == null) { this.onSetNextWriteCallbacks = new CopyOnWriteArrayList<>(); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/OpenemsTypeDoc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/OpenemsTypeDoc.java index b07fc91bf5e..c5fd4a7bb74 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/OpenemsTypeDoc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/OpenemsTypeDoc.java @@ -1,7 +1,9 @@ package io.openems.edge.common.channel.internal; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.ChannelCategory; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.DoubleDoc; import io.openems.edge.common.channel.FloatDoc; @@ -9,7 +11,6 @@ import io.openems.edge.common.channel.LongDoc; import io.openems.edge.common.channel.ShortDoc; import io.openems.edge.common.channel.StringDoc; -import io.openems.edge.common.channel.Unit; public abstract class OpenemsTypeDoc extends AbstractDoc { @@ -37,6 +38,11 @@ protected OpenemsTypeDoc(OpenemsType type) { super(type); } + @Override + public ChannelCategory getChannelCategory() { + return ChannelCategory.OPENEMS_TYPE; + } + /** * Sets the Access-Mode for the Channel. * diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannel.java index 084caf8dc8c..62b5a89a07c 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannel.java @@ -10,10 +10,10 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import io.openems.common.channel.Level; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.ChannelId; import io.openems.edge.common.channel.EnumReadChannel; -import io.openems.edge.common.channel.Level; import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannelDoc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannelDoc.java index 1de8fe3755d..d7d500f9929 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannelDoc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/StateCollectorChannelDoc.java @@ -1,9 +1,9 @@ package io.openems.edge.common.channel.internal; +import io.openems.common.channel.Level; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.EnumDoc; -import io.openems.edge.common.channel.Level; import io.openems.edge.common.component.OpenemsComponent; public class StateCollectorChannelDoc extends EnumDoc { diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/value/Value.java b/io.openems.edge.common/src/io/openems/edge/common/channel/value/Value.java index f57acd11e0c..a3dab062c4f 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/value/Value.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/value/Value.java @@ -6,9 +6,9 @@ import io.openems.common.exceptions.InvalidValueException; import io.openems.common.types.OpenemsType; +import io.openems.common.types.OptionsEnum; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.EnumDoc; -import io.openems.edge.common.channel.OptionsEnum; import io.openems.edge.common.type.TypeUtils; /** diff --git a/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java b/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java index 30daf930457..f617c40736a 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java +++ b/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java @@ -34,6 +34,7 @@ public abstract class AbstractOpenemsComponent implements OpenemsComponent { private final Map> channels = Collections.synchronizedMap(new HashMap<>()); private String id = null; + private String alias = null; private ComponentContext componentContext = null; private boolean enabled = true; @@ -61,6 +62,7 @@ public abstract class AbstractOpenemsComponent implements OpenemsComponent { * } * * + *

    * Note: the separation in firstInitialChannelIds and furtherInitialChannelIds * is only there to enforce that calling the constructor cannot be forgotten. * This way it needs to be called with at least one parameter - which is always @@ -84,15 +86,23 @@ protected AbstractOpenemsComponent(io.openems.edge.common.channel.ChannelId[] fi * * @param context the OSGi ComponentContext * @param id the unique OpenEMS Component ID + * @param alias Human-readable name of this Component. Typically + * 'config.alias()'. Defaults to 'id' if empty * @param enabled is the Component enabled? */ - protected void activate(ComponentContext context, String id, boolean enabled) { - if (id == null || id.trim().equals("")) { + protected void activate(ComponentContext context, String id, String alias, boolean enabled) { + if (id == null || id.trim().isEmpty()) { this.id = "_component" + AbstractOpenemsComponent.NEXT_GENERATED_COMPONENT_ID.incrementAndGet(); } else { this.id = id; } + if (alias == null || alias.trim().isEmpty()) { + this.alias = this.id; + } else { + this.alias = alias; + } + this.enabled = enabled; this.componentContext = context; if (isEnabled()) { @@ -192,6 +202,11 @@ public String id() { return this.id; } + @Override + public String alias() { + return this.alias; + } + @Override public Channel _channel(String channelName) { Channel channel = this.channels.get(channelName); diff --git a/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java b/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java index 9fb6ec32224..b2b689cd560 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java +++ b/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java @@ -2,18 +2,19 @@ import java.util.List; +import io.openems.common.OpenemsConstants; +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.ChannelAddress; import io.openems.common.types.EdgeConfig; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; /** * A Service that provides access to OpenEMS-Components. */ -public interface ComponentManager { +public interface ComponentManager extends OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { CONFIG_NOT_ACTIVATED(Doc.of(Level.WARNING) // @@ -42,12 +43,15 @@ public Doc doc() { * Gets a OpenEMS-Component by its Component-ID. * * @param componentId the Component-ID (e.g. "_sum") - * @param the typed Component + * @param the typed Component * @return the OpenEMS-Component * @throws OpenemsNamedException if the Component was not found */ @SuppressWarnings("unchecked") public default T getComponent(String componentId) throws OpenemsNamedException { + if (componentId == OpenemsConstants.COMPONENT_MANAGER_ID) { + return (T) this; + } List components = this.getComponents(); for (OpenemsComponent component : components) { if (component.id().equals(componentId)) { @@ -61,7 +65,7 @@ public default T getComponent(String componentId) t * Gets a Channel by its Channel-Address. * * @param channelAddress the Channel-Address - * @param the typed Channel + * @param the typed Channel * @return the Channel * @throws IllegalArgumentException if the Channel is not available * @throws OpenemsNamedException on error diff --git a/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java b/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java index 13d7fab9509..010b4f5aab2 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java +++ b/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java @@ -9,6 +9,7 @@ import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; +import io.openems.common.channel.AccessMode; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.internal.StateCollectorChannel; @@ -47,6 +48,13 @@ public interface OpenemsComponent { */ public String id(); + /** + * Returns a human-readable name of this Component.. + * + * @return the human-readable name + */ + public String alias(); + /** * Returns whether this component is enabled. * @@ -95,7 +103,7 @@ default String servicePid() { * Returns a Channel defined by its ChannelId string representation. * * @param channelName the Channel-ID as a string - * @param the expected typed Channel + * @param the expected typed Channel * @throws IllegalArgumentException on error * @return the Channel or throw Exception */ @@ -121,11 +129,13 @@ default > T channel(String channelName) throws IllegalArgum /** * Returns a Channel defined by its ChannelId. * - * @param the Type of the Channel. See {@link Doc#getType()} + * @param the Type of the Channel. See {@link Doc#getType()} * @param channelId the Channel-ID * @return the Channel + * @throws IllegalArgumentException on error */ - default > T channel(io.openems.edge.common.channel.ChannelId channelId) { + default > T channel(io.openems.edge.common.channel.ChannelId channelId) + throws IllegalArgumentException { T channel = this.channel(channelId.id()); return channel; } @@ -153,8 +163,8 @@ public Doc doc() { } } - public static ModbusSlaveNatureTable getModbusSlaveNatureTable() { - return ModbusSlaveNatureTable.of(OpenemsComponent.class, 80) // + public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) { + return ModbusSlaveNatureTable.of(OpenemsComponent.class, accessMode, 80) // .channel(0, ChannelId.STATE, ModbusType.UINT16) // .build(); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/meta/Meta.java b/io.openems.edge.common/src/io/openems/edge/common/meta/Meta.java index 32ea42ebf85..16c7f8cbe93 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/meta/Meta.java +++ b/io.openems.edge.common/src/io/openems/edge/common/meta/Meta.java @@ -1,6 +1,7 @@ package io.openems.edge.common.meta; import io.openems.common.OpenemsConstants; +import io.openems.common.channel.AccessMode; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.modbusslave.ModbusSlave; @@ -32,9 +33,9 @@ public Doc doc() { } @Override - public default ModbusSlaveTable getModbusSlaveTable() { + public default ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable( // - ModbusSlaveNatureTable.of(Meta.class, 199) // + ModbusSlaveNatureTable.of(Meta.class, accessMode, 199) // .uint16(0, "OpenEMS Version Major", OpenemsConstants.VERSION_MAJOR) // .uint16(1, "OpenEMS Version Minor", OpenemsConstants.VERSION_MINOR) // .uint16(2, "OpenEMS Version Patch", OpenemsConstants.VERSION_PATCH) // diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecord.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecord.java index 35a7988e25f..cf58b2cd032 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecord.java +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecord.java @@ -1,6 +1,7 @@ package io.openems.edge.common.modbusslave; -import io.openems.edge.common.channel.Unit; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; public abstract class ModbusRecord { @@ -32,15 +33,16 @@ public String getComponentId() { } public abstract String getName(); - + public abstract String getValueDescription(); - + public Unit getUnit() { return Unit.NONE; } - + public abstract byte[] getValue(OpenemsComponent component); public abstract void writeValue(OpenemsComponent component, int index, byte byte1, byte byte2); + public abstract AccessMode getAccessMode(); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordChannel.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordChannel.java index c893bf2af8c..7854ac920fe 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordChannel.java @@ -1,15 +1,20 @@ package io.openems.edge.common.modbusslave; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.types.ChannelAddress; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.ChannelId; -import io.openems.edge.common.channel.Unit; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.channel.EnumDoc; import io.openems.edge.common.component.OpenemsComponent; public class ModbusRecordChannel extends ModbusRecord { @@ -17,6 +22,7 @@ public class ModbusRecordChannel extends ModbusRecord { private final Logger log = LoggerFactory.getLogger(ModbusRecordChannel.class); private final ChannelId channelId; + private final AccessMode accessMode; protected Consumer onWriteValueCallback = null; @@ -26,9 +32,10 @@ public class ModbusRecordChannel extends ModbusRecord { */ private final Byte[] writeValueBuffer; - public ModbusRecordChannel(int offset, ModbusType type, ChannelId channelId) { + public ModbusRecordChannel(int offset, ModbusType type, ChannelId channelId, AccessMode modbusApiAccessMode) { super(offset, type); this.channelId = channelId; + this.accessMode = evaluateActualAccessMode(channelId, modbusApiAccessMode); // initialize buffer int byteLength = 0; @@ -49,6 +56,47 @@ public ModbusRecordChannel(int offset, ModbusType type, ChannelId channelId) { this.writeValueBuffer = new Byte[byteLength]; } + /** + * Evaluate the AccessMode from configured Modbus-Api-AccessMode and + * Channel-AccessMode. + * + * @param channelId + * @param channelAccessMode + * @return + */ + private static AccessMode evaluateActualAccessMode(ChannelId channelId, AccessMode modbusApiAccessMode) { + AccessMode channelAccessMode = channelId.doc().getAccessMode(); + switch (modbusApiAccessMode) { + case READ_ONLY: + switch (channelAccessMode) { + case READ_ONLY: + case READ_WRITE: + case WRITE_ONLY: + return AccessMode.READ_ONLY; + } + case READ_WRITE: + switch (channelAccessMode) { + case READ_ONLY: + return AccessMode.READ_ONLY; + case READ_WRITE: + return AccessMode.READ_WRITE; + case WRITE_ONLY: + return AccessMode.WRITE_ONLY; + } + case WRITE_ONLY: + switch (channelAccessMode) { + case READ_ONLY: + return AccessMode.READ_ONLY; + case READ_WRITE: + case WRITE_ONLY: + return AccessMode.WRITE_ONLY; + } + } + // should never come here + assert (true); + return AccessMode.READ_ONLY; + } + public ChannelId getChannelId() { return channelId; } @@ -65,13 +113,37 @@ public byte[] getValue(OpenemsComponent component) { Object value = channel.value().get(); switch (this.getType()) { case FLOAT32: - return ModbusRecordFloat32.toByteArray(value); + switch (this.accessMode) { + case READ_ONLY: + case READ_WRITE: + return ModbusRecordFloat32.toByteArray(value); + case WRITE_ONLY: + return ModbusRecordFloat32Reserved.UNDEFINED_VALUE; + } case FLOAT64: - return ModbusRecordFloat64.toByteArray(value); + switch (this.accessMode) { + case READ_ONLY: + case READ_WRITE: + return ModbusRecordFloat64.toByteArray(value); + case WRITE_ONLY: + return ModbusRecordFloat64Reserved.UNDEFINED_VALUE; + } case STRING16: - return ModbusRecordString16.toByteArray(value); + switch (this.accessMode) { + case READ_ONLY: + case READ_WRITE: + return ModbusRecordString16.toByteArray(value); + case WRITE_ONLY: + return ModbusRecordString16Reserved.UNDEFINED_VALUE; + } case UINT16: - return ModbusRecordUint16.toByteArray(value); + switch (this.accessMode) { + case READ_ONLY: + case READ_WRITE: + return ModbusRecordUint16.toByteArray(value); + case WRITE_ONLY: + return ModbusRecordUint16Reserved.UNDEFINED_VALUE; + } } assert true; return new byte[0]; @@ -83,6 +155,16 @@ public void onWriteValue(Consumer onWriteValueCallback) { @Override public void writeValue(OpenemsComponent component, int index, byte byte1, byte byte2) { + switch (this.accessMode) { + case READ_ONLY: + // Read-Only Access enabled. Do not write value. + return; + + case READ_WRITE: + case WRITE_ONLY: + break; + } + this.writeValueBuffer[index * 2] = byte1; this.writeValueBuffer[index * 2 + 1] = byte2; // is the buffer full? @@ -141,7 +223,23 @@ public Unit getUnit() { @Override public String getValueDescription() { + Doc doc = this.channelId.doc(); + if (doc instanceof EnumDoc) { + // List possible Options for this Enum + EnumDoc d = (EnumDoc) doc; + return Arrays.stream(d.getOptions()) // + .map(option -> { + return option.getValue() + ":" + option.getName(); + }) // + .collect(Collectors.joining(", ")); + } + return ""; // TODO get some meaningful text from Doc(), like 'between 0 and 100 %' } + @Override + public AccessMode getAccessMode() { + return this.accessMode; + } + } \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordConstant.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordConstant.java index fadf423daaa..5062e33f837 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordConstant.java +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordConstant.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.common.channel.AccessMode; import io.openems.edge.common.component.OpenemsComponent; public abstract class ModbusRecordConstant extends ModbusRecord { @@ -41,4 +42,9 @@ public String getName() { return this.name; } + @Override + public AccessMode getAccessMode() { + return AccessMode.READ_ONLY; + } + } diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordString16Reserved.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordString16Reserved.java new file mode 100644 index 00000000000..3803976e6e5 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordString16Reserved.java @@ -0,0 +1,14 @@ +package io.openems.edge.common.modbusslave; + +public class ModbusRecordString16Reserved extends ModbusRecordString16 { + + public ModbusRecordString16Reserved(int offset) { + super(offset, "", null); + } + + @Override + public String toString() { + return "ModbusRecordString16Reserved [type=" + getType() + "]"; + } + +} diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16.java index 1558cfce28b..ee97363f202 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16.java +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16.java @@ -28,7 +28,8 @@ public static byte[] toByteArray(short value) { } public static byte[] toByteArray(Object value) { - if (value == null) { + if (value == null || (value instanceof io.openems.common.types.OptionsEnum + && ((io.openems.common.types.OptionsEnum) value).isUndefined())) { return UNDEFINED_VALUE; } else { return toByteArray((short) TypeUtils.getAsType(OpenemsType.SHORT, value)); @@ -37,7 +38,7 @@ public static byte[] toByteArray(Object value) { @Override public String getValueDescription() { - return this.value != null ? this.value + "/0x" + Integer.toHexString(this.value) : ""; + return this.value != null ? Short.toString(this.value) : ""; } } diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16Hash.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16Hash.java index a033eecab8f..eedcce0caf9 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16Hash.java +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusRecordUint16Hash.java @@ -15,4 +15,9 @@ public String toString() { + Integer.toHexString(this.value) + ", type=" + getType() + "]"; } + @Override + public String getValueDescription() { + return "0x" + Integer.toHexString(this.value & 0xffff); + } + } diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlave.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlave.java index 5c9e0364450..4817a7f5394 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlave.java +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlave.java @@ -1,9 +1,16 @@ package io.openems.edge.common.modbusslave; +import io.openems.common.channel.AccessMode; import io.openems.edge.common.component.OpenemsComponent; public interface ModbusSlave extends OpenemsComponent { - public ModbusSlaveTable getModbusSlaveTable(); + /** + * Gets the Modbus-Slave-Table for this OpenEMS-Component. + * + * @param accessMode filters the Modbus-Records that should be shown + * @return the Modbus-Slave-Table + */ + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlaveNatureTable.java b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlaveNatureTable.java index 54258826b03..49592800c6f 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlaveNatureTable.java +++ b/io.openems.edge.common/src/io/openems/edge/common/modbusslave/ModbusSlaveNatureTable.java @@ -4,24 +4,55 @@ import java.util.Collections; import java.util.List; +import io.openems.common.channel.AccessMode; import io.openems.edge.common.channel.ChannelId; public final class ModbusSlaveNatureTable { public static class Builder { private final Class nature; + private final AccessMode accessModeFilter; private final int length; private final List maps = new ArrayList<>(); private int nextOffset = 0; - public Builder(Class nature, int length) { + public Builder(Class nature, AccessMode accessModeFilter, int length) { this.nature = nature; + this.accessModeFilter = accessModeFilter; this.length = length; } public Builder channel(int offset, ChannelId channelId, ModbusType type) { - this.add(new ModbusRecordChannel(offset, type, channelId)); + AccessMode filter = this.accessModeFilter; + AccessMode channel = channelId.doc().getAccessMode(); + if ( + // Filter for READ_ONLY Channels + (filter == AccessMode.READ_ONLY && (channel == AccessMode.READ_ONLY || channel == AccessMode.READ_WRITE)) || // + // Filter for READ_WRITE channels -> allow all Channels + (filter == AccessMode.READ_WRITE) || // + // Filter for WRITE_ONLY channels + (filter == AccessMode.WRITE_ONLY + && (channel == AccessMode.WRITE_ONLY || channel == AccessMode.READ_WRITE))) { + this.add(new ModbusRecordChannel(offset, type, channelId, filter)); + + } else { + // Channel did not pass filter -> show as Reserved + switch (type) { + case FLOAT32: + this.float32Reserved(offset); + break; + case FLOAT64: + this.float64Reserved(offset); + break; + case STRING16: + this.string16Reserved(offset); + break; + case UINT16: + this.uint16Reserved(offset); + break; + } + } return this; } @@ -50,11 +81,26 @@ public Builder float32Reserved(int offset) { return this; } + public Builder float64(int offset, String name, double value) { + this.add(new ModbusRecordFloat64(offset, name, value)); + return this; + } + + public Builder float64Reserved(int offset) { + this.add(new ModbusRecordFloat64Reserved(offset)); + return this; + } + public Builder string16(int offset, String name, String value) { this.add(new ModbusRecordString16(offset, name, value)); return this; } + public Builder string16Reserved(int offset) { + this.add(new ModbusRecordString16Reserved(offset)); + return this; + } + private void add(ModbusRecord record) throws IllegalArgumentException { if (record.getOffset() != this.nextOffset) { throw new IllegalArgumentException("Expected offset [" + this.nextOffset + "] but got [" @@ -71,10 +117,11 @@ public ModbusSlaveNatureTable build() { }); return new ModbusSlaveNatureTable(nature, length, this.maps.toArray(new ModbusRecord[this.maps.size()])); } + } - public static Builder of(Class nature, int length) { - return new Builder(nature, length); + public static Builder of(Class nature, AccessMode accessMode, int length) { + return new Builder(nature, accessMode, length); } private final Class nature; diff --git a/io.openems.edge.common/src/io/openems/edge/common/sum/GridMode.java b/io.openems.edge.common/src/io/openems/edge/common/sum/GridMode.java index 9158a18b7ac..e3dcff84e04 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/sum/GridMode.java +++ b/io.openems.edge.common/src/io/openems/edge/common/sum/GridMode.java @@ -1,6 +1,6 @@ package io.openems.edge.common.sum; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum GridMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java b/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java index 7aab2c45981..a0fd4772ff2 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java +++ b/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java @@ -1,10 +1,11 @@ package io.openems.edge.common.sum; import io.openems.common.OpenemsConstants; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; import io.openems.edge.common.modbusslave.ModbusType; @@ -273,6 +274,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Wh * */ + // TODO rename to Actual_Energy PRODUCTION_DC_ACTIVE_ENERGY(Doc.of(OpenemsType.LONG) // .unit(Unit.WATT_HOURS)), /** @@ -298,8 +300,8 @@ public Doc doc() { } } - public static ModbusSlaveNatureTable getModbusSlaveNatureTable() { - return ModbusSlaveNatureTable.of(Sum.class, 220) // + public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) { + return ModbusSlaveNatureTable.of(Sum.class, accessMode, 220) // .channel(0, ChannelId.ESS_SOC, ModbusType.UINT16) // .channel(1, ChannelId.ESS_ACTIVE_POWER, ModbusType.FLOAT32) // .float32Reserved(3) // ChannelId.ESS_MIN_ACTIVE_POWER diff --git a/io.openems.edge.common/src/io/openems/edge/common/taskmanager/MetaTasksManager.java b/io.openems.edge.common/src/io/openems/edge/common/taskmanager/MetaTasksManager.java new file mode 100644 index 00000000000..0b6b63ed73e --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/taskmanager/MetaTasksManager.java @@ -0,0 +1,112 @@ +package io.openems.edge.common.taskmanager; + +import java.util.EnumMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; + +/** + * Manages a number of {@link TasksManager}s. + * + *

    + * A useful application for MetaTasksManager is to provide a list of Tasks that + * need to be handled on an OpenEMS Cycle run. + * + * @param + */ +public class MetaTasksManager { + + private final Multimap> tasksManagers = Multimaps + .synchronizedListMultimap(ArrayListMultimap.create()); + private Map> nextTasks; + + public MetaTasksManager() { + // initialize Queues for next tasks + EnumMap> nextTasks = new EnumMap<>(Priority.class); + for (Priority priority : Priority.values()) { + nextTasks.put(priority, new LinkedList<>()); + } + this.nextTasks = nextTasks; + } + + /** + * Adds a TasksManager. + * + * @param sourceId a source identifier + * @param task the TasksManager + */ + public synchronized void addTasksManager(String sourceId, TasksManager tasksManager) { + this.tasksManagers.put(sourceId, tasksManager); + } + + /** + * Removes a TasksManager. + * + * @param sourceId a source identifier + * @param task the TasksManager + */ + public synchronized void removeTasksManager(String sourceId, TasksManager tasksManager) { + this.tasksManagers.remove(sourceId, tasksManager); + } + + /** + * Removes all TasksManagers with the given Source-ID. + * + * @param sourceId a source identifier + */ + public synchronized void removeTasksManager(String sourceId) { + this.tasksManagers.removeAll(sourceId); + } + + /** + * Gets one task that with the given Priority sequentially. + * + * @return the next task; null if there are no tasks with the given Priority + */ + public synchronized T getOneTask(Priority priority) { + Queue tasks = this.nextTasks.get(priority); + if (tasks.isEmpty()) { + // refill the queue + for (TasksManager tasksManager : this.tasksManagers.values()) { + tasks.addAll(tasksManager.getAllTasks(priority)); + } + } + + // returns the head or 'null' if the queue is still empty after refilling it + return tasks.poll(); + } + + /** + * Gets all Tasks with the given Priority by their Source-ID. + * + * @param priority the priority + * @return a list of tasks + */ + public Multimap getAllTasksBySourceId(Priority priority) { + Multimap result = ArrayListMultimap.create(); + for (Entry> entry : this.tasksManagers.entries()) { + result.putAll(entry.getKey(), entry.getValue().getAllTasks(priority)); + } + return result; + } + + /** + * Gets all Tasks with by their Source-ID. + * + * @param priority the priority + * @return a list of tasks + */ + public Multimap getAllTasksBySourceId() { + Multimap result = ArrayListMultimap.create(); + for (Entry> entry : this.tasksManagers.entries()) { + result.putAll(entry.getKey(), entry.getValue().getAllTasks()); + } + return result; + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/taskmanager/TasksManager.java b/io.openems.edge.common/src/io/openems/edge/common/taskmanager/TasksManager.java index c88dca63b9d..838b19f32f5 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/taskmanager/TasksManager.java +++ b/io.openems.edge.common/src/io/openems/edge/common/taskmanager/TasksManager.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.EnumMap; import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -28,6 +29,7 @@ public class TasksManager { private final Queue nextOnceTasks = new LinkedList<>(); private int nextTaskIndex = 0; + private EnumMap nextTaskIndexPerPriority = new EnumMap<>(Priority.class); @SafeVarargs public TasksManager(T... tasks) { @@ -46,6 +48,17 @@ public final synchronized void addTasks(T... tasks) { } } + /** + * Adds multiple Tasks. + * + * @param tasks an array of Tasks + */ + public void addTasks(List tasks) { + for (T task : tasks) { + this.addTask(task); + } + } + /** * Adds a Task, taking its Priority in consideration. * @@ -90,6 +103,37 @@ public synchronized void removeTask(T task) { } } + /** + * Clears all Tasks lists. + */ + public synchronized void clearAll() { + this.allTasks.clear(); + this.prioHighTasks.clear(); + this.prioLowTasks.clear(); + this.nextLowTasks.clear(); + this.prioOnceTasks.clear(); + this.nextOnceTasks.clear(); + } + + /** + * Get all tasks with the given Priority. + * + * @param priority the Priority + * @return a list of Tasks + */ + public synchronized List getAllTasks(Priority priority) { + switch (priority) { + case HIGH: + return Collections.unmodifiableList(this.prioHighTasks); + case LOW: + return Collections.unmodifiableList(this.prioLowTasks); + case ONCE: + return Collections.unmodifiableList(this.prioOnceTasks); + } + assert true; + return new ArrayList<>(); + } + /** * Gets the next Tasks. This should normally be called once per Cycle. * @@ -148,4 +192,38 @@ public synchronized T getOneTask() { } return this.allTasks.get(this.nextTaskIndex++); } + + /** + * Gets one task that is lower than the given Priority sequentially. + * + * @return the next task; null if there are no tasks with the given Priority + */ + public synchronized T getOneTask(Priority priority) { + List tasks = this.getAllTasks(priority); + if (tasks.isEmpty()) { + return null; + } + Integer nextTaskIndex = this.nextTaskIndexPerPriority.get(priority); + if (nextTaskIndex == null) { + // start new + nextTaskIndex = 0; + } + // reached end of list -> start over? + if (nextTaskIndex > tasks.size() - 1) { + switch (priority) { + case HIGH: + case LOW: + // start over + nextTaskIndex = 0; + break; + case ONCE: + // do not start over + return null; + } + } + + this.nextTaskIndexPerPriority.put(priority, nextTaskIndex + 1); + return tasks.get(nextTaskIndex); + } + } \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentConfig.java b/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentConfig.java index e3dba1fcf34..bed20d8e2ff 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentConfig.java +++ b/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentConfig.java @@ -25,6 +25,10 @@ public String id() { return this.id; } + public String alias() { + return this.id; + } + public boolean enabled() { return DEFAULT_ENABLED; } @@ -32,5 +36,5 @@ public boolean enabled() { public String webconsole_configurationFactory_nameHint() { return ""; } - + } diff --git a/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java b/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java index 8f198c42b9f..b4cd4dee105 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java +++ b/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java @@ -1,10 +1,15 @@ package io.openems.edge.common.test; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; +import org.osgi.service.component.ComponentContext; + +import io.openems.common.OpenemsConstants; import io.openems.common.types.EdgeConfig; +import io.openems.edge.common.channel.Channel; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; @@ -39,4 +44,34 @@ public EdgeConfig getEdgeConfig() { return new EdgeConfig(); } + @Override + public String id() { + return OpenemsConstants.COMPONENT_MANAGER_ID; + } + + @Override + public String alias() { + return OpenemsConstants.COMPONENT_MANAGER_ID; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public ComponentContext getComponentContext() { + return null; + } + + @Override + public Channel _channel(String channelName) { + return null; + } + + @Override + public Collection> channels() { + return new ArrayList<>(); + } + } \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java b/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java index be9f0785950..b18dbb68b39 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java +++ b/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java @@ -7,7 +7,7 @@ import com.google.gson.JsonPrimitive; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; import io.openems.edge.common.channel.value.Value; /** diff --git a/io.openems.edge.common/test/io/openems/edge/common/channel/ChannelTest.java b/io.openems.edge.common/test/io/openems/edge/common/channel/ChannelTest.java index 119349eced3..5804a741e3e 100644 --- a/io.openems.edge.common/test/io/openems/edge/common/channel/ChannelTest.java +++ b/io.openems.edge.common/test/io/openems/edge/common/channel/ChannelTest.java @@ -6,6 +6,7 @@ import org.junit.Test; +import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; public class ChannelTest { diff --git a/io.openems.edge.common/test/io/openems/edge/common/channel/TestOptions.java b/io.openems.edge.common/test/io/openems/edge/common/channel/TestOptions.java index eae9112273b..b5024c2908a 100644 --- a/io.openems.edge.common/test/io/openems/edge/common/channel/TestOptions.java +++ b/io.openems.edge.common/test/io/openems/edge/common/channel/TestOptions.java @@ -1,5 +1,7 @@ package io.openems.edge.common.channel; +import io.openems.common.types.OptionsEnum; + public enum TestOptions implements OptionsEnum { UNDEFINED(-1, "Undefined"), // OPTION_1(1, "Option 1"), // diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java index 7dd0b3cebd3..aaf03e7f3f9 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java @@ -5,13 +5,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; import org.ops4j.pax.logging.spi.PaxAppender; import org.ops4j.pax.logging.spi.PaxLoggingEvent; -import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.cm.ConfigurationEvent; import org.osgi.service.cm.ConfigurationListener; import org.osgi.service.component.ComponentContext; @@ -61,8 +58,9 @@ public class BackendApi extends AbstractOpenemsComponent protected final BackendWorker worker = new BackendWorker(this); + protected final ApiWorker apiWorker = new ApiWorker(); + private final Logger log = LoggerFactory.getLogger(BackendApi.class); - private final ApiWorker apiWorker = new ApiWorker(); protected WebsocketClient websocket = null; protected int noOfCycles = DEFAULT_NO_OF_CYCLES; // default, is going to be overwritten by config @@ -72,20 +70,11 @@ public class BackendApi extends AbstractOpenemsComponent private boolean isSystemLogSubscribed = false; @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) - protected volatile Timedata timedata = null; + private volatile Timedata timedata = null; @Reference protected ComponentManager componentManager; - @Reference - private ConfigurationAdmin configAdmin; - - @Reference(policy = ReferencePolicy.DYNAMIC, // - policyOption = ReferencePolicyOption.GREEDY, // - cardinality = ReferenceCardinality.MULTIPLE, // - target = "(&(enabled=true)(!(service.factoryPid=" + COMPONENT_NAME + ")))") - private volatile List components = new CopyOnWriteArrayList<>(); - public enum ChannelId implements io.openems.edge.common.channel.ChannelId { ; private final Doc doc; @@ -110,7 +99,7 @@ public BackendApi() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); this.noOfCycles = config.noOfCycles(); this.debug = config.debug(); @@ -164,10 +153,6 @@ public void run() throws OpenemsNamedException { this.apiWorker.run(); } - public ComponentManager getComponentManager() { - return this.componentManager; - } - @Override protected void logInfo(Logger log, String message) { super.logInfo(log, message); diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java index ddde0de16fc..99698341576 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java @@ -11,12 +11,12 @@ import com.google.common.collect.EvictingQueue; import com.google.gson.JsonElement; +import io.openems.common.channel.AccessMode; import io.openems.common.jsonrpc.base.JsonrpcMessage; import io.openems.common.jsonrpc.notification.TimestampedDataNotification; import io.openems.common.types.ChannelAddress; import io.openems.common.types.OpenemsType; import io.openems.common.worker.AbstractCycleWorker; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.EnumReadChannel; import io.openems.edge.common.type.slidingvalue.DoubleSlidingValue; import io.openems.edge.common.type.slidingvalue.FloatSlidingValue; diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java index 91c19975e0a..f7026656f05 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java @@ -9,8 +9,14 @@ name = "Controller Api Backend", // description = "This controller connects to OpenEMS Backend") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ctrlBackend0"; + @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 = "Apikey", description = "Apikey for authentication at OpenEMS Backend.") diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java index 3e87ec5d5af..550f1621c4b 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java @@ -23,7 +23,7 @@ public void run(WebSocket ws, JsonObject handshake) { this.parent.logInfo(this.log, "Connected to OpenEMS Backend"); // Immediately send Config - EdgeConfig config = this.parent.getComponentManager().getEdgeConfig(); + EdgeConfig config = this.parent.componentManager.getEdgeConfig(); EdgeConfigNotification message = new EdgeConfigNotification(config); this.parent.websocket.sendMessage(message); diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnRequest.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnRequest.java index d9647efeac2..a61c9ef2f97 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnRequest.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnRequest.java @@ -18,9 +18,11 @@ import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; +import io.openems.common.jsonrpc.request.SetChannelValueRequest; import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; import io.openems.common.jsonrpc.response.AuthenticatedRpcResponse; +import io.openems.common.session.Role; import io.openems.common.session.User; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.jsonapi.JsonApi; @@ -79,6 +81,10 @@ private CompletableFuture handleAuthenticatedRpcReques resultFuture = this.handleDeleteComponentConfigRequest(user, DeleteComponentConfigRequest.from(request)); break; + case SetChannelValueRequest.METHOD: + resultFuture = this.handleSetChannelValueRequest(user, SetChannelValueRequest.from(request)); + break; + case ComponentJsonApiRequest.METHOD: resultFuture = this.handleComponentJsonApiRequest(user, ComponentJsonApiRequest.from(request)); break; @@ -126,6 +132,8 @@ private CompletableFuture handleGetEdgeConfigRequ */ private CompletableFuture handleCreateComponentConfigRequest(User user, CreateComponentConfigRequest createComponentConfigRequest) throws OpenemsNamedException { + user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.INSTALLER); + // wrap original request inside ComponentJsonApiRequest String componentId = OpenemsConstants.COMPONENT_MANAGER_ID; ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, createComponentConfigRequest); @@ -143,6 +151,8 @@ private CompletableFuture handleCreateComponentCo */ private CompletableFuture handleUpdateComponentConfigRequest(User user, UpdateComponentConfigRequest updateComponentConfigRequest) throws OpenemsNamedException { + user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.OWNER); + // wrap original request inside ComponentJsonApiRequest String componentId = OpenemsConstants.COMPONENT_MANAGER_ID; ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, updateComponentConfigRequest); @@ -160,6 +170,8 @@ private CompletableFuture handleUpdateComponentCo */ private CompletableFuture handleDeleteComponentConfigRequest(User user, DeleteComponentConfigRequest deleteComponentConfigRequest) throws OpenemsNamedException { + user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.INSTALLER); + // wrap original request inside ComponentJsonApiRequest String componentId = OpenemsConstants.COMPONENT_MANAGER_ID; ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, deleteComponentConfigRequest); @@ -167,6 +179,21 @@ private CompletableFuture handleDeleteComponentCo return this.handleComponentJsonApiRequest(user, request); } + /** + * Handles a SetChannelValueRequest. + * + * @param user the User + * @param request the SetChannelValueRequest + * @return the Future JSON-RPC Response + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleSetChannelValueRequest(User user, + SetChannelValueRequest request) throws OpenemsNamedException { + user.assertRoleIsAtLeast(SetChannelValueRequest.METHOD, Role.ADMIN); + + return this.parent.apiWorker.handleSetChannelValueRequest(this.parent.componentManager, user, request); + } + /** * Handles a ComponentJsonApiRequest. * diff --git a/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/ApiWorker.java b/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/ApiWorker.java index 802b1c085e3..a5f36c27b63 100644 --- a/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/ApiWorker.java +++ b/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/ApiWorker.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -11,9 +12,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.SetChannelValueRequest; +import io.openems.common.session.User; +import io.openems.common.types.OpenemsType; +import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.WriteChannel; +import io.openems.edge.common.component.ComponentManager; /** * Takes care of continuously writing channels till a timeout. This class is @@ -42,12 +51,55 @@ public ApiWorker() { } public void addValue(WriteChannel channel, WriteObject writeObject) { - log.info("Set [" + channel.address() + "] to [" + writeObject.valueToString() + "] via API. Timeout is [" - + this.timeoutSeconds + "s]"); this.resetTimeout(); synchronized (this.values) { - this.values.put(channel, writeObject); + if (writeObject.isNull()) { + // set null -> remove write-value + log.info("Unset [" + channel.address() + "] via API."); + this.values.remove(channel); + } else { + // set write-value + log.info("Set [" + channel.address() + "] to [" + writeObject.valueToString() + + "] via API. Timeout is [" + this.timeoutSeconds + "s]"); + this.values.put(channel, writeObject); + } + } + } + + /** + * Adds a value via JSON-RPC SetChannelValueRequest. + * + * @param user the authenticated User + * @param request the Request + * @return success + * @throws OpenemsNamedException on error + * @throws IllegalArgumentException on error + */ + public CompletableFuture handleSetChannelValueRequest(ComponentManager componentManager, + User user, SetChannelValueRequest request) throws IllegalArgumentException, OpenemsNamedException { + // check for writable channel + Channel channel = componentManager.getChannel(request.getChannelAddress()); + if (!(channel instanceof WriteChannel)) { + throw new OpenemsException("[" + channel + "] is not a Write Channel"); } + + // parse value + Object value; + if (request.getValue().isJsonNull()) { + value = null; + } else { + value = JsonUtils.getAsBestType(request.getValue()); + if (value instanceof String && ((String) value).isEmpty() + && channel.channelId().doc().getType() != OpenemsType.STRING) { + // Allow non-string Channels to be set to 'UNDEFINED' using an empty string + value = null; + } + } + + // set value + this.addValue((WriteChannel) channel, new WritePojo(value)); + + return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } private synchronized void resetTimeout() { diff --git a/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WriteObject.java b/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WriteObject.java index efef7e5b9aa..888688b155e 100644 --- a/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WriteObject.java +++ b/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WriteObject.java @@ -5,6 +5,7 @@ import java.util.function.Consumer; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.channel.WriteChannel; public abstract class WriteObject { @@ -62,7 +63,9 @@ public void notifyTimeout() { this.onTimeoutCallbacks.forEach(callback -> callback.run()); } - public abstract void setNextWriteValue(WriteChannel writeChannel) throws OpenemsException; + public abstract void setNextWriteValue(WriteChannel writeChannel) throws OpenemsNamedException; public abstract String valueToString(); + + public abstract boolean isNull(); } diff --git a/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WritePojo.java b/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WritePojo.java index b356a4e45a7..432868c44d1 100644 --- a/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WritePojo.java +++ b/io.openems.edge.controller.api.core/src/io/openems/edge/controller/api/core/WritePojo.java @@ -1,6 +1,6 @@ package io.openems.edge.controller.api.core; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.channel.WriteChannel; /** @@ -16,13 +16,18 @@ public WritePojo(Object value) { } @Override - public void setNextWriteValue(WriteChannel writeChannel) throws OpenemsException { + public void setNextWriteValue(WriteChannel writeChannel) throws OpenemsNamedException { writeChannel.setNextWriteValueFromObject(this.value); } @Override public String valueToString() { - return this.value.toString(); + return String.valueOf(this.value); + } + + @Override + public boolean isNull() { + return this.value == null; } } diff --git a/io.openems.edge.controller.api.modbus/bnd.bnd b/io.openems.edge.controller.api.modbus/bnd.bnd index 15776bcb38d..8d958f9b875 100644 --- a/io.openems.edge.controller.api.modbus/bnd.bnd +++ b/io.openems.edge.controller.api.modbus/bnd.bnd @@ -3,12 +3,13 @@ Bundle-Vendor: FENECON GmbH Bundle-License: https://opensource.org/licenses/EPL-2.0 Bundle-Version: 1.0.0.${tstamp} Export-Package: io.openems.edge.controller.api -Private-Package: \ - io.openems.edge.controller.api.modbus +Private-Package: io.openems.edge.controller.api.modbus,\ + io.openems.edge.controller.api.modbus.jsonrpc -includeresource: {readme.md} --buildpath: ${buildpath},\ +-buildpath: \ + ${buildpath},\ io.openems.common;version=latest,\ io.openems.edge.common;version=latest,\ io.openems.edge.controller.api;version=latest,\ @@ -16,9 +17,10 @@ Private-Package: \ io.openems.edge.timedata.api;version=latest,\ com.ghgande.j2mod;version=2.5,\ com.google.gson;version=2.8,\ - slf4j.api + slf4j.api,\ + io.openems.wrapper.fastexcel;version=latest -testpath: ${testpath} javac.source: 1.8 -javac.target: 1.8 \ No newline at end of file +javac.target: 1.8 diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/Config.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/Config.java index e5c1e611595..9ecf83ba6d1 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/Config.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/Config.java @@ -3,17 +3,28 @@ import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; -@ObjectClassDefinition( // +import io.openems.common.channel.AccessMode; + +@ObjectClassDefinition(// name = "Controller Api Modbus/TCP", // description = "This controller provides a Modbus/TCP api.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ctrlApiModbusTcp0"; + @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 = "Port", description = "Port on which the server should listen.") int port() default ModbusTcpApi.DEFAULT_PORT; + @AttributeDefinition(name = "Access-Mode", description = "Only allow access to Read-Only/Read-Write/Write-Only channels.") + AccessMode accessMode() default AccessMode.READ_WRITE; + @AttributeDefinition(name = "Component-IDs", description = "Components that should be made available via Modbus.") String[] component_ids() default { "_sum" }; diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/ModbusTcpApi.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/ModbusTcpApi.java index 2db28b605e7..fc2e8abd944 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/ModbusTcpApi.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/ModbusTcpApi.java @@ -23,6 +23,7 @@ import com.ghgande.j2mod.modbus.slave.ModbusSlaveFactory; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; @@ -30,7 +31,6 @@ import io.openems.common.worker.AbstractWorker; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; import io.openems.edge.common.channel.WriteChannel; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; @@ -47,6 +47,10 @@ import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.core.ApiWorker; import io.openems.edge.controller.api.core.WritePojo; +import io.openems.edge.controller.api.modbus.jsonrpc.GetModbusProtocolExportXlsxRequest; +import io.openems.edge.controller.api.modbus.jsonrpc.GetModbusProtocolExportXlsxResponse; +import io.openems.edge.controller.api.modbus.jsonrpc.GetModbusProtocolRequest; +import io.openems.edge.controller.api.modbus.jsonrpc.GetModbusProtocolResponse; import io.openems.edge.timedata.api.Timedata; @Designate(ocd = Config.class, factory = true) @@ -56,9 +60,9 @@ configurationPolicy = ConfigurationPolicy.REQUIRE) public class ModbusTcpApi extends AbstractOpenemsComponent implements Controller, OpenemsComponent, JsonApi { - public final static int UNIT_ID = 1; - public final static int DEFAULT_PORT = 502; - public final static int DEFAULT_MAX_CONCURRENT_CONNECTIONS = 5; + public static final int UNIT_ID = 1; + public static final int DEFAULT_PORT = 502; + public static final int DEFAULT_MAX_CONCURRENT_CONNECTIONS = 5; private final Logger log = LoggerFactory.getLogger(ModbusTcpApi.class); @@ -66,10 +70,16 @@ public class ModbusTcpApi extends AbstractOpenemsComponent implements Controller private final MyProcessImage processImage; /** - * Holds the link between Modbus address and ModbusRecord + * Holds the link between Modbus address and ModbusRecord. */ protected final TreeMap records = new TreeMap<>(); + /** + * Holds the link between Modbus start address of a Component and the + * Component-ID. + */ + protected final TreeMap components = new TreeMap<>(); + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) protected Meta metaComponent = null; @@ -101,9 +111,7 @@ public Doc doc() { } protected volatile Map _components = new HashMap<>(); - private String[] componentIds = new String[0]; - private int port = ModbusTcpApi.DEFAULT_PORT; - private int maxConcurrentConnections = ModbusTcpApi.DEFAULT_MAX_CONCURRENT_CONNECTIONS; + private Config config = null; public ModbusTcpApi() { super(// @@ -117,16 +125,14 @@ public ModbusTcpApi() { @Activate void activate(ComponentContext context, Config config) throws ModbusException, OpenemsException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); + this.config = config; // update filter for 'components' if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "Component", config.component_ids())) { return; } - this.port = config.port(); - this.maxConcurrentConnections = config.maxConcurrentConnections(); - this.componentIds = config.component_ids(); this.apiWorker.setTimeoutSeconds(config.apiTimeout()); if (!this.isEnabled()) { @@ -153,27 +159,28 @@ protected void deactivate() { private final AbstractWorker startApiWorker = new AbstractWorker() { - private final static int DEFAULT_WAIT_TIME = 5000; // 5 seconds + private static final int DEFAULT_WAIT_TIME = 5000; // 5 seconds private com.ghgande.j2mod.modbus.slave.ModbusSlave slave = null; @Override protected void forever() { + int port = ModbusTcpApi.this.config.port(); if (this.slave == null) { try { // start new server - this.slave = ModbusSlaveFactory.createTCPSlave(ModbusTcpApi.this.port, - ModbusTcpApi.this.maxConcurrentConnections); + this.slave = ModbusSlaveFactory.createTCPSlave(port, + ModbusTcpApi.this.config.maxConcurrentConnections()); slave.addProcessImage(UNIT_ID, ModbusTcpApi.this.processImage); slave.open(); - ModbusTcpApi.this.logInfo(ModbusTcpApi.this.log, "Modbus/TCP Api started on port [" - + ModbusTcpApi.this.port + "] with UnitId [" + ModbusTcpApi.UNIT_ID + "]."); + ModbusTcpApi.this.logInfo(ModbusTcpApi.this.log, "Modbus/TCP Api started on port [" + port + + "] with UnitId [" + ModbusTcpApi.UNIT_ID + "]."); ModbusTcpApi.this.channel(ChannelId.UNABLE_TO_START).setNextValue(false); } catch (ModbusException e) { ModbusSlaveFactory.close(); - ModbusTcpApi.this.logError(ModbusTcpApi.this.log, "Unable to start Modbus/TCP Api on port [" - + ModbusTcpApi.this.port + "]: " + e.getMessage()); + ModbusTcpApi.this.logError(ModbusTcpApi.this.log, + "Unable to start Modbus/TCP Api on port [" + port + "]: " + e.getMessage()); ModbusTcpApi.this.channel(ChannelId.UNABLE_TO_START).setNextValue(true); } @@ -182,7 +189,7 @@ protected void forever() { String error = slave.getError(); if (error != null) { ModbusTcpApi.this.logError(ModbusTcpApi.this.log, - "Unable to start Modbus/TCP Api on port [" + ModbusTcpApi.this.port + "]: " + error); + "Unable to start Modbus/TCP Api on port [" + port + "]: " + error); ModbusTcpApi.this.channel(ChannelId.UNABLE_TO_START).setNextValue(true); this.slave = null; // stop server @@ -207,7 +214,7 @@ private void initializeModbusRecords() { nextAddress = this.addMetaComponentToProcessImage(nextAddress); // add remaining components; sorted by configured componentIds - for (String id : this.componentIds) { + for (String id : this.config.component_ids()) { // find next component in order ModbusSlave component = this._components.get(id); if (component == null) { @@ -221,14 +228,14 @@ private void initializeModbusRecords() { } /** - * Adds the Meta-Component to the Process Image + * Adds the Meta-Component to the Process Image. * - * @param startAddress - * @return + * @param startAddress the start-address + * @return the next start-address */ private int addMetaComponentToProcessImage(int startAddress) { ModbusSlave component = this.metaComponent; - ModbusSlaveTable table = component.getModbusSlaveTable(); + ModbusSlaveTable table = component.getModbusSlaveTable(this.config.accessMode()); // add the Component-Model Length int nextAddress = this.addRecordToProcessImage(startAddress, @@ -244,14 +251,15 @@ private int addMetaComponentToProcessImage(int startAddress) { } /** - * Adds a Component to the Process Image + * Adds a Component to the Process Image. * - * @param startAddress - * @param component - * @return + * @param startAddress the start-address + * @param component the OpenEMS Component + * @return the next start-address */ private int addComponentToProcessImage(int startAddress, ModbusSlave component) { - ModbusSlaveTable table = component.getModbusSlaveTable(); + this.components.put(startAddress, component.alias()); + ModbusSlaveTable table = component.getModbusSlaveTable(this.config.accessMode()); // add the Component-ID and Component-Model Length int nextAddress = this.addRecordToProcessImage(startAddress, @@ -282,10 +290,11 @@ private int addComponentToProcessImage(int startAddress, ModbusSlave component) } /** - * Adds a Record to the process image at the given address + * Adds a Record to the process image at the given address. * - * @param address - * @param record + * @param address the address + * @param record the record + * @param component the OpenEMS Component * @return the next address after this record */ private int addRecordToProcessImage(int address, ModbusRecord record, OpenemsComponent component) { @@ -335,6 +344,11 @@ public CompletableFuture handleJsonrpcRequest(User user, switch (message.getMethod()) { case GetModbusProtocolRequest.METHOD: return CompletableFuture.completedFuture(new GetModbusProtocolResponse(message.getId(), this.records)); + + case GetModbusProtocolExportXlsxRequest.METHOD: + return CompletableFuture.completedFuture( + new GetModbusProtocolExportXlsxResponse(message.getId(), this.components, this.records)); + } return null; } diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyProcessImage.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyProcessImage.java index d709d367192..567a14d68b8 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyProcessImage.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyProcessImage.java @@ -92,10 +92,10 @@ public synchronized Register getRegister(int ref) throws MyIllegalAddressExcepti } /** - * Get value as byte-array and convert it to InputRegisters + * Get value as byte-array and convert it to InputRegisters. * - * @param record - * @return + * @param record the record + * @return the Register */ private Register[] getRecordValueRegisters(ModbusRecord record) { MyRegister[] result = new MyRegister[record.getType().getWords()]; @@ -114,9 +114,7 @@ private Register[] getRecordValueRegisters(ModbusRecord record) { } /********************************************** - * - * From here, the methods are not implemented! - * + * From here, the methods are not implemented!. ********************************************** */ diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyRegister.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyRegister.java index d27fbe9b46d..2d70150ec25 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyRegister.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/MyRegister.java @@ -6,8 +6,7 @@ /** * Largely copied from - * {@link com.ghgande.j2mod.modbus.procimg.SynchronizedAbstractRegister} - * + * {@link com.ghgande.j2mod.modbus.procimg.SynchronizedAbstractRegister}. */ public class MyRegister implements Register { diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxRequest.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxRequest.java new file mode 100644 index 00000000000..0d702b0be8f --- /dev/null +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxRequest.java @@ -0,0 +1,38 @@ +package io.openems.edge.controller.api.modbus.jsonrpc; + +import java.util.UUID; + +import com.google.gson.JsonObject; + +import io.openems.common.jsonrpc.base.JsonrpcRequest; + +/** + * Exports the Modbus Protocol to an Excel (xlsx) file. + * + *

    + * {
    + *   "jsonrpc": "2.0",
    + *   "id": "UUID",
    + *   "method": "getModbusProtocolExportXlsx",
    + *   "params": {}
    + * }
    + * 
    + */ +public class GetModbusProtocolExportXlsxRequest extends JsonrpcRequest { + + public static final String METHOD = "getModbusProtocolExportXlsx"; + + public GetModbusProtocolExportXlsxRequest() { + this(UUID.randomUUID()); + } + + public GetModbusProtocolExportXlsxRequest(UUID id) { + super(id, METHOD); + } + + @Override + public JsonObject getParams() { + return new JsonObject(); + } + +} diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxResponse.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxResponse.java new file mode 100644 index 00000000000..79d97c434ed --- /dev/null +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxResponse.java @@ -0,0 +1,182 @@ +package io.openems.edge.controller.api.modbus.jsonrpc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.UUID; + +import org.dhatim.fastexcel.Color; +import org.dhatim.fastexcel.Workbook; +import org.dhatim.fastexcel.Worksheet; + +import io.openems.common.channel.Unit; +import io.openems.common.jsonrpc.response.Base64PayloadResponse; +import io.openems.edge.common.modbusslave.ModbusRecord; +import io.openems.edge.common.modbusslave.ModbusRecordFloat32; +import io.openems.edge.common.modbusslave.ModbusRecordFloat64; +import io.openems.edge.common.modbusslave.ModbusRecordString16; +import io.openems.edge.common.modbusslave.ModbusRecordUint16; +import io.openems.edge.common.modbusslave.ModbusType; + +/** + * Represents a JSON-RPC Response for 'getModbusProtocolExportXlsx'. + * + *

    + * + *

    + * {
    + *   "jsonrpc": "2.0",
    + *   "id": "UUID",
    + *   "result": {
    + *     "payload": Base64-String
    + *   }
    + * }
    + * 
    + */ +public class GetModbusProtocolExportXlsxResponse extends Base64PayloadResponse { + + public GetModbusProtocolExportXlsxResponse(UUID id, TreeMap components, + TreeMap records) { + super(id, generatePayload(components, records)); + } + + private static final int COL_ADDRESS = 0; + private static final int COL_DESCRIPTION = 1; + private static final int COL_TYPE = 2; + private static final int COL_VALUE = 3; + private static final int COL_UNIT = 4; + private static final int COL_ACCESS = 5; + + private static byte[] generatePayload(TreeMap components, TreeMap records) { + byte[] payload = new byte[0]; + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + Workbook wb = new Workbook(os, "OpenEMS Modbus-TCP", "1.0"); + Worksheet ws = wb.newWorksheet("Modbus-Table"); + + ws.width(COL_ADDRESS, 10); + ws.width(COL_DESCRIPTION, 25); + ws.width(COL_TYPE, 10); + ws.width(COL_VALUE, 35); + ws.width(COL_UNIT, 20); + ws.width(COL_ACCESS, 10); + // Add headers + addSheetHeader(wb, ws); + // Create Sheet + int nextRow = 1; + for (Entry entry : records.entrySet()) { + int address = entry.getKey(); + + String component = components.get(address); + if (address == 0 || component != null) { + if (address == 0) { + // Add the global header row + addComponentHeader(ws, "Header", nextRow); + } else { + // Add Component-Header-Row + addComponentHeader(ws, component, nextRow); + } + nextRow++; + } + + // Add a Record-Row + ModbusRecord record = entry.getValue(); + if (nextRow % 2 == 0) { + addRecord(ws, address, record, nextRow); + } else { + addRecord(ws, address, record, nextRow); + } + nextRow++; + } + // Shading alternative Rows + ws.range(1, 0, nextRow, 5).style().borderStyle("thin").shadeAlternateRows(Color.GRAY1).set(); + // Add undefined values sheet + addUndefinedSheet(wb); + wb.finish(); + os.flush(); + payload = os.toByteArray(); + os.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return payload; + } + + public static void addSheetHeader(Workbook workbook, Worksheet sheet) { + + sheet.value(0, COL_ADDRESS, "Address"); + sheet.value(0, COL_DESCRIPTION, "Description"); + sheet.value(0, COL_TYPE, "Type"); + sheet.value(0, COL_VALUE, "Value/Range"); + sheet.value(0, COL_UNIT, "Unit"); + sheet.value(0, COL_ACCESS, "Access"); + sheet.style(0, 0).bold().fillColor(Color.GRAY5).borderStyle("thin"); + } + + public static void addComponentHeader(Worksheet sheet, String title, int rowCount) { + sheet.value(rowCount, 0, title); + sheet.style(rowCount, 0).bold().fillColor(Color.GRAY10).borderStyle("thin"); + } + + public static void addRecord(Worksheet sheet, int address, ModbusRecord record, int rowCount) { + sheet.value(rowCount, COL_ADDRESS, address); + sheet.value(rowCount, COL_DESCRIPTION, record.getName()); + sheet.value(rowCount, COL_TYPE, record.getType().toString()); + sheet.value(rowCount, COL_VALUE, record.getValueDescription()); + Unit unit = record.getUnit(); + if (unit != Unit.NONE) { + sheet.value(rowCount, COL_UNIT, unit.toString()); + } + sheet.value(rowCount, COL_ACCESS, record.getAccessMode().getAbbreviation()); + } + + /** + * Add Sheet to describe UNDEFINED values. + * + * @param workbook the Workbook + */ + private static void addUndefinedSheet(Workbook wb) { + Worksheet ws = wb.newWorksheet("Undefined values"); + + ws.value(0, COL_ADDRESS, "In case a modbus value is 'undefined', the following value will be read:"); + ws.value(1, 0, "type"); + ws.value(1, 1, "value"); + + int nextRow = 2; + for (ModbusType modbusType : ModbusType.values()) { + byte[] value = new byte[0]; + switch (modbusType) { + case FLOAT32: + value = ModbusRecordFloat32.UNDEFINED_VALUE; + break; + case FLOAT64: + value = ModbusRecordFloat64.UNDEFINED_VALUE; + break; + case STRING16: + value = ModbusRecordString16.UNDEFINED_VALUE; + break; + case UINT16: + value = ModbusRecordUint16.UNDEFINED_VALUE; + break; + } + nextRow++; + ws.value(nextRow, 0, modbusType.toString()); + ws.value(nextRow, 1, byteArrayToString(value)); + //Alternate Row shading + ws.range(1, 0, nextRow, 2).style().borderStyle("thin").shadeAlternateRows(Color.GRAY1).set(); + } + } + + private static String byteArrayToString(byte[] value) { + if (value.length == 0) { + return ""; + } + StringBuilder result = new StringBuilder("0x"); + for (byte b : value) { + result.append(Integer.toHexString(b & 0xff)); + } + return result.toString(); + } + +} diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/GetModbusProtocolRequest.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolRequest.java similarity index 82% rename from io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/GetModbusProtocolRequest.java rename to io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolRequest.java index 333ef5996e2..ed07c9415c2 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/GetModbusProtocolRequest.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolRequest.java @@ -1,4 +1,4 @@ -package io.openems.edge.controller.api.modbus; +package io.openems.edge.controller.api.modbus.jsonrpc; import java.util.UUID; @@ -8,7 +8,7 @@ /** * Wraps a JSON-RPC Request to query the Modbus Protocol from Modbus/TCP - * Api-Controller + * Api-Controller. * *
      * {
    @@ -21,7 +21,7 @@
      */
     public class GetModbusProtocolRequest extends JsonrpcRequest {
     
    -	public final static String METHOD = "getModbusProtocol";
    +	public static final String METHOD = "getModbusProtocol";
     
     	public GetModbusProtocolRequest() {
     		this(UUID.randomUUID());
    diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/GetModbusProtocolResponse.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolResponse.java
    similarity index 87%
    rename from io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/GetModbusProtocolResponse.java
    rename to io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolResponse.java
    index b3a645480e8..1ed17eae342 100644
    --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/GetModbusProtocolResponse.java
    +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolResponse.java
    @@ -1,4 +1,4 @@
    -package io.openems.edge.controller.api.modbus;
    +package io.openems.edge.controller.api.modbus.jsonrpc;
     
     import java.util.Map.Entry;
     import java.util.TreeMap;
    @@ -7,13 +7,15 @@
     import com.google.gson.JsonArray;
     import com.google.gson.JsonObject;
     
    +import io.openems.common.channel.Unit;
     import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess;
     import io.openems.common.utils.JsonUtils;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.modbusslave.ModbusRecord;
     
     /**
    - * Wraps a JSON-RPC Response to "getModbusProtocol" Request
    + * Wraps a JSON-RPC Response to "getModbusProtocol" Request.
    + * 
    + * 

    * *

      * {
    @@ -55,6 +57,7 @@ public JsonObject getResult() {
     					.addProperty("value", record.getValueDescription()) //
     					.addProperty("unit", record.getUnit() == Unit.NONE ? "" : record.getUnit().toString()) //
     					.addProperty("type", record.getType().toString()) //
    +					.addProperty("access", record.getAccessMode().getAbbreviation()) //
     					.build());
     		}
     		JsonObject j = new JsonObject();
    diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/Config.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/Config.java
    index 11dd0ba2fd5..b88a387e8c2 100644
    --- a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/Config.java
    +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/Config.java
    @@ -8,8 +8,13 @@
     		description = "This controller provides a REST/JSON api.")
     @interface Config {
     
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlApiRest0";
     
    +	@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 = "Port", description = "Port on which the webserver should listen.")
    diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestApi.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestApi.java
    index 005acd67aca..67456c72ed6 100644
    --- a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestApi.java
    +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestApi.java
    @@ -64,7 +64,7 @@ public RestApi() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) throws OpenemsException {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     
     		if (!this.isEnabled()) {
     			// abort if disabled
    diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java
    index e827092269d..66f3bbb6d96 100644
    --- a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java
    +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java
    @@ -39,6 +39,7 @@
     import io.openems.common.jsonrpc.request.CreateComponentConfigRequest;
     import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest;
     import io.openems.common.jsonrpc.request.GetEdgeConfigRequest;
    +import io.openems.common.jsonrpc.request.SetChannelValueRequest;
     import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest;
     import io.openems.common.session.Role;
     import io.openems.common.session.User;
    @@ -50,7 +51,6 @@
     import io.openems.edge.common.component.OpenemsComponent;
     import io.openems.edge.common.jsonapi.JsonApi;
     import io.openems.edge.common.user.EdgeUser;
    -import io.openems.edge.controller.api.core.WritePojo;
     
     public class RestHandler extends AbstractHandler {
     
    @@ -159,23 +159,13 @@ private void handleChannel(User user, List targets, Request baseRequest,
     		// get request attributes
     		ChannelAddress channelAddress = new ChannelAddress(targets.get(0), targets.get(1));
     
    -		// get channel
    -		Channel channel;
    -		try {
    -			channel = this.parent.componentManager.getChannel(channelAddress);
    -		} catch (IllegalArgumentException e) {
    -			this.parent.logWarn(this.log, e.getMessage());
    -			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
    -			return;
    -		}
    -
     		// call handler methods
     		switch (request.getMethod()) {
     		case "GET":
    -			this.handleGet(user, channel, baseRequest, request, response);
    +			this.handleGet(user, channelAddress, baseRequest, request, response);
     			break;
     		case "POST":
    -			this.handlePost(user, channel, baseRequest, request, response);
    +			this.handlePost(user, channelAddress, baseRequest, request, response);
     			break;
     		}
     	}
    @@ -183,17 +173,27 @@ private void handleChannel(User user, List targets, Request baseRequest,
     	/**
     	 * Handles HTTP GET request.
     	 * 
    -	 * @param user        the User
    -	 * @param channel     the affected channel
    -	 * @param baseRequest the HTTP POST base-request
    -	 * @param request     the HTTP POST request
    -	 * @param response    the result to be returned
    +	 * @param user           the User
    +	 * @param channelAddress the ChannelAddress
    +	 * @param baseRequest    the HTTP POST base-request
    +	 * @param request        the HTTP POST request
    +	 * @param response       the result to be returned
     	 * @throws OpenemsNamedException
     	 */
    -	private void handleGet(User user, Channel channel, Request baseRequest, HttpServletRequest request,
    +	private void handleGet(User user, ChannelAddress channelAddress, Request baseRequest, HttpServletRequest request,
     			HttpServletResponse response) throws OpenemsNamedException {
     		user.assertRoleIsAtLeast("HTTP GET", Role.GUEST);
     
    +		// get channel
    +		Channel channel;
    +		try {
    +			channel = this.parent.componentManager.getChannel(channelAddress);
    +		} catch (IllegalArgumentException e) {
    +			this.parent.logWarn(this.log, e.getMessage());
    +			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
    +			return;
    +		}
    +
     		JsonObject j = new JsonObject();
     		j.add("value", channel.value().asJson());
     		// type
    @@ -222,22 +222,17 @@ private void sendOkResponse(Request baseRequest, HttpServletResponse response, J
     	/**
     	 * Handles HTTP POST request.
     	 *
    -	 * @param user        the User
    -	 * @param channel     the affected channel
    -	 * @param baseRequest the HTTP POST base-request
    -	 * @param request     the HTTP POST request
    -	 * @param response    the result to be returned
    +	 * @param user           the User
    +	 * @param channelAddress the ChannelAddress
    +	 * @param baseRequest    the HTTP POST base-request
    +	 * @param request        the HTTP POST request
    +	 * @param response       the result to be returned
     	 * @throws OpenemsNamedException
     	 */
    -	private void handlePost(User user, Channel channel, Request baseRequest, HttpServletRequest request,
    +	private void handlePost(User user, ChannelAddress channelAddress, Request baseRequest, HttpServletRequest request,
     			HttpServletResponse response) throws OpenemsNamedException {
     		user.assertRoleIsAtLeast("HTTP POST", Role.ADMIN);
     
    -		// check for writable channel
    -		if (!(channel instanceof WriteChannel)) {
    -			throw new OpenemsException("[" + channel + "] is not a Write Channel");
    -		}
    -
     		// parse json
     		JsonObject jHttpPost = RestHandler.parseJson(baseRequest);
     
    @@ -249,15 +244,9 @@ private void handlePost(User user, Channel channel, Request baseRequest, Http
     			throw new OpenemsException("Value is missing");
     		}
     
    -		// set channel value
    -		Object value;
    -		if (jValue.isJsonNull()) {
    -			value = null;
    -		} else {
    -			value = jValue.toString();
    -		}
    -		this.parent.apiWorker.addValue((WriteChannel) channel, new WritePojo(value));
    -		log.info("Updated Channel [" + channel.address() + "] to value [" + jValue.toString() + "].");
    +		// send request to apiworker
    +		this.parent.apiWorker.handleSetChannelValueRequest(this.parent.componentManager, user,
    +				new SetChannelValueRequest(channelAddress.getComponentId(), channelAddress.getChannelId(), jValue));
     
     		this.sendOkResponse(baseRequest, response, new JsonObject());
     	}
    diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java
    index d0a76dfb528..cc861c598f6 100644
    --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java
    +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Api Websocket", //
     		description = "This controller provides an HTTP Websocket/JSON api. It is required for OpenEMS UI.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlApiWebsocket0";
     
    +	@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 = "Port", description = "Port on which the Websocket server should listen.")
    diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java
    index a4cbb7ddb0c..f00cb4ba612 100644
    --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java
    +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java
    @@ -9,6 +9,7 @@
     
     import com.google.gson.JsonObject;
     
    +import io.openems.common.jsonrpc.notification.AuthenticateWithSessionIdFailedNotification;
     import io.openems.common.jsonrpc.notification.AuthenticateWithSessionIdNotification;
     import io.openems.edge.common.user.EdgeUser;
     
    @@ -62,11 +63,10 @@ public void run(WebSocket ws, JsonObject handshake) {
     		}
     		wsData.setSessionToken(token);
     
    -		// if we are here, automatic authentication was not possible -> notify client
    -		// TODO // send authentication notification
    -		// AuthenticateWithSessionIdNotification notification = new
    -		// AuthenticateWithSessionIdNotification(
    -		// token, Utils.getEdgeMetadata(user.getRole()));
    +		if (!wsData.getUser().isPresent()) {
    +			// automatic authentication was not possible -> notify client
    +			this.parent.server.sendMessage(ws, new AuthenticateWithSessionIdFailedNotification());
    +		}
     	}
     
     }
    diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java
    index 00b2aff2123..e56da245036 100644
    --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java
    +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java
    @@ -28,6 +28,7 @@
     import io.openems.common.jsonrpc.request.GetEdgeConfigRequest;
     import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesDataRequest;
     import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesEnergyRequest;
    +import io.openems.common.jsonrpc.request.SetChannelValueRequest;
     import io.openems.common.jsonrpc.request.SubscribeChannelsRequest;
     import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest;
     import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest;
    @@ -125,6 +126,10 @@ private CompletableFuture handleEdgeRpcRequest(WsData wsData, E
     			resultFuture = this.handleGetEdgeConfigRequest(user, GetEdgeConfigRequest.from(request));
     			break;
     
    +		case SetChannelValueRequest.METHOD:
    +			resultFuture = this.handleSetChannelValueRequest(user, SetChannelValueRequest.from(request));
    +			break;
    +
     		case ComponentJsonApiRequest.METHOD:
     			resultFuture = this.handleComponentJsonApiRequest(user, ComponentJsonApiRequest.from(request));
     			break;
    @@ -292,6 +297,21 @@ private CompletableFuture handleGetEdgeConfigRequest(Edg
     		return this.handleComponentJsonApiRequest(user, request);
     	}
     
    +	/**
    +	 * Handles a SetChannelValueRequest.
    +	 * 
    +	 * @param user    the User
    +	 * @param request the SetChannelValueRequest
    +	 * @return the Future JSON-RPC Response
    +	 * @throws OpenemsNamedException on error
    +	 */
    +	private CompletableFuture handleSetChannelValueRequest(EdgeUser user,
    +			SetChannelValueRequest request) throws OpenemsNamedException {
    +		user.assertRoleIsAtLeast(SetChannelValueRequest.METHOD, Role.ADMIN);
    +
    +		return this.parent.apiWorker.handleSetChannelValueRequest(this.parent.componentManager, user, request);
    +	}
    +
     	/**
     	 * Handles a ComponentJsonApiRequest.
     	 * 
    diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java
    index be052dc02ca..5be5ed5e4ac 100644
    --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java
    +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java
    @@ -27,6 +27,7 @@
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
     import io.openems.common.exceptions.OpenemsException;
     import io.openems.common.jsonrpc.notification.EdgeConfigNotification;
    +import io.openems.common.jsonrpc.notification.EdgeRpcNotification;
     import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest;
     import io.openems.common.types.EdgeConfig;
     import io.openems.edge.common.channel.Doc;
    @@ -54,7 +55,8 @@ public class WebsocketApi extends AbstractOpenemsComponent
     
     	public static final int DEFAULT_PORT = 8075;
     
    -	private final ApiWorker apiWorker = new ApiWorker();
    +	protected final ApiWorker apiWorker = new ApiWorker();
    +
     	private final SystemLogHandler systemLogHandler;
     
     	protected WebsocketServer server = null;
    @@ -98,7 +100,7 @@ public WebsocketApi() {
     
     	@Activate
     	protected void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		if (!this.isEnabled()) {
     			// abort if disabled
     			return;
    @@ -189,7 +191,7 @@ protected void handleSubscribeSystemLogRequest(UUID token, SubscribeSystemLogReq
     	public void configurationEvent(ConfigurationEvent event) {
     		EdgeConfig config = this.componentManager.getEdgeConfig();
     		EdgeConfigNotification message = new EdgeConfigNotification(config);
    -		this.server.broadcastMessage(message);
    +		this.server.broadcastMessage(new EdgeRpcNotification(WebsocketApi.EDGE_ID, message));
     	}
     
     	/**
    diff --git a/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java b/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java
    index d2777c70ce2..43e16b6bc56 100644
    --- a/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java
    +++ b/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java
    @@ -2,9 +2,9 @@
     
     import org.osgi.annotation.versioning.ProviderType;
     
    +import io.openems.common.channel.Level;
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.Level;
     import io.openems.edge.common.channel.StateChannel;
     import io.openems.edge.common.component.OpenemsComponent;
     
    diff --git a/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/Config.java b/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/Config.java
    index 22849bbd7c3..d1b027c7f44 100644
    --- a/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/Config.java
    +++ b/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Balancing Cos-Phi Asymmetric", //
     		description = "Keeps the Grid meter on a defined Cos-Phi.")
     @interface Config {
    +	
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlBalancingCosPhi0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/CosPhi.java b/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/CosPhi.java
    index cc924ea8bb6..96977d694b1 100644
    --- a/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/CosPhi.java
    +++ b/io.openems.edge.controller.asymmetric.balancingcosphi/src/io/openems/edge/controller/asymmetric/balancingcosphi/CosPhi.java
    @@ -68,7 +68,7 @@ public CosPhi() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     
     		this.essId = config.ess_id();
     		this.meterId = config.meter_id();
    diff --git a/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/AsymmetricFixActivePower.java b/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/AsymmetricFixActivePower.java
    index 02f8b78c313..0e7aeb4ecf6 100644
    --- a/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/AsymmetricFixActivePower.java
    +++ b/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/AsymmetricFixActivePower.java
    @@ -57,7 +57,7 @@ public AsymmetricFixActivePower() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/Config.java b/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/Config.java
    index abe73d40043..614a22e3e16 100644
    --- a/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/Config.java
    +++ b/io.openems.edge.controller.asymmetric.fixactivepower/src/io/openems/edge/controller/asymmetric/fixactivepower/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Fix Active Power Asymmetric", //
     		description = "Defines a fixed charge/discharge power to an asymmetric energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlFixActivePower0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/AsymmetricFixReactivePower.java b/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/AsymmetricFixReactivePower.java
    index 19924f939b9..3f4206adc62 100644
    --- a/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/AsymmetricFixReactivePower.java
    +++ b/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/AsymmetricFixReactivePower.java
    @@ -57,7 +57,7 @@ public AsymmetricFixReactivePower() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/Config.java b/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/Config.java
    index bdd04ba955b..f087f2e9011 100644
    --- a/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/Config.java
    +++ b/io.openems.edge.controller.asymmetric.fixreactivepower/src/io/openems/edge/controller/asymmetric/fixreactivepower/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Fix Reactive Power Asymmetric", //
     		description = "Defines a fixed charge/discharge power to an asymmetric energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlFixReactivePower0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/Config.java b/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/Config.java
    index 8d178af5958..c88c10c747b 100644
    --- a/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/Config.java
    +++ b/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Phase-Rectification", //
     		description = "Sets the ess to the required activepower to get all three phases on the meter to the same level.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlPhaseRectification0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/PhaseRectification.java b/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/PhaseRectification.java
    index c4fcdbf7c63..3d31e51c4d2 100644
    --- a/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/PhaseRectification.java
    +++ b/io.openems.edge.controller.asymmetric.phaserectification/src/io/openems/edge/controller/asymmetric/phaserectification/PhaseRectification.java
    @@ -58,7 +58,7 @@ public PhaseRectification() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/ChannelThreshold.java b/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/ChannelThreshold.java
    index 71def2204cd..c18ef81746e 100644
    --- a/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/ChannelThreshold.java
    +++ b/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/ChannelThreshold.java
    @@ -77,7 +77,7 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept
     		this.inputChannelAddress = ChannelAddress.fromString(config.inputChannelAddress());
     		this.outputChannelAddress = ChannelAddress.fromString(config.outputChannelAddress());
     
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/Config.java b/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/Config.java
    index 96438970d4b..6381cac62ad 100644
    --- a/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/Config.java
    +++ b/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Channel Threshold", //
     		description = "This controller switches a Digital Output channel ON, if the value of the input channel is within a configured threshold. This behaviour can be inverted using the 'invert' config option.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlChannelThreshold0";
     
    +	@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 = "Input Channel", description = "Address of the input channel. If the value of this channel is within a configured threshold, the output channel is switched ON.")
    diff --git a/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/State.java b/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/State.java
    index 2c81e377389..3f964200ec3 100644
    --- a/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/State.java
    +++ b/io.openems.edge.controller.channelthreshold/src/io/openems/edge/controller/channelthreshold/State.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.controller.channelthreshold;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum State implements OptionsEnum {
     	/**
    diff --git a/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/Config.java b/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/Config.java
    index e4efc222f82..1d111ff608e 100644
    --- a/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/Config.java
    +++ b/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/Config.java
    @@ -6,10 +6,15 @@
     @ObjectClassDefinition( //
     		name = "Controller CHP SOC", //
     		description = "This is a Controller for CHP (Combined Heat and Power Unit, German: BHKW - Blockheizkraftwerk). The Controller is used to signal CHP turn ON or turn OFF when the battery is empty or battery is full respectively, based on the SoC percentage")
    -
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlChpSoc0";
     
    +	@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 = "Input Channel", description = "Address of the input channel")
    diff --git a/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/ControllerChpSoc.java b/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/ControllerChpSoc.java
    index 2d3725c724f..0e8979b4c69 100644
    --- a/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/ControllerChpSoc.java
    +++ b/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/ControllerChpSoc.java
    @@ -74,7 +74,7 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept
     		this.inputChannelAddress = ChannelAddress.fromString(config.inputChannelAddress());
     		this.outputChannelAddress = ChannelAddress.fromString(config.outputChannelAddress());
     
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.alias(), config.id(), config.enabled());
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/State.java b/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/State.java
    index 4b366d976d1..11a6e799cae 100644
    --- a/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/State.java
    +++ b/io.openems.edge.controller.chp.soc/src/io/openems/edge/controller/chp/soc/State.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.controller.chp.soc;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum State implements OptionsEnum {
     	/**
    diff --git a/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/Config.java b/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/Config.java
    index f801c3fc304..f4f42050a71 100644
    --- a/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/Config.java
    +++ b/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Debug Detailed Log", //
     		description = "This controller prints detailed information about the defined components on the console")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlDetailedLog0";
     
    +	@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 = "Component-IDs", description = "IDs of OpenemsComponents.")
    diff --git a/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/DebugDetailedLog.java b/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/DebugDetailedLog.java
    index c99a07f156a..eb63eff1e26 100644
    --- a/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/DebugDetailedLog.java
    +++ b/io.openems.edge.controller.debug.detailedlog/src/io/openems/edge/controller/debug/detailedlog/DebugDetailedLog.java
    @@ -73,7 +73,7 @@ public DebugDetailedLog() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/Config.java b/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/Config.java
    index 6659b5ffba6..8809d1bbeb8 100644
    --- a/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/Config.java
    +++ b/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/Config.java
    @@ -1,13 +1,20 @@
     package io.openems.edge.controller.debuglog;
     
    +import org.osgi.service.metatype.annotations.AttributeDefinition;
     import org.osgi.service.metatype.annotations.ObjectClassDefinition;
     
     @ObjectClassDefinition( //
     		name = "Controller Debug Log", //
     		description = "This controller prints information about all available components on the console")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlDebugLog0";
     
    +	@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;
     
     	String webconsole_configurationFactory_nameHint() default "Controller Debug Log [{id}]";
    diff --git a/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/DebugLog.java b/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/DebugLog.java
    index 83d8148ff01..745de329fcd 100644
    --- a/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/DebugLog.java
    +++ b/io.openems.edge.controller.debug.log/src/io/openems/edge/controller/debuglog/DebugLog.java
    @@ -62,7 +62,7 @@ public DebugLog() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/Config.java b/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/Config.java
    index adcb3a5f152..eeb5f632ffc 100644
    --- a/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/Config.java
    +++ b/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Discharge Limit Considering Cell Voltage", //
     		description = "Limits discharge for a battery considering the minimal cell voltage.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlDischargeLimitConsideringCellVoltage0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/DischargeLimitConsideringCellVoltage.java b/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/DischargeLimitConsideringCellVoltage.java
    index 0b98d3ca630..e7e75d90a0c 100644
    --- a/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/DischargeLimitConsideringCellVoltage.java
    +++ b/io.openems.edge.controller.dischargelimitconsideringcellvoltage/src/io/openems/edge/controller/dischargelimitconsideringcellvoltage/DischargeLimitConsideringCellVoltage.java
    @@ -16,9 +16,9 @@
     import org.slf4j.LoggerFactory;
     
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.types.OptionsEnum;
     import io.openems.edge.battery.api.Battery;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.OptionsEnum;
     import io.openems.edge.common.component.AbstractOpenemsComponent;
     import io.openems.edge.common.component.ComponentManager;
     import io.openems.edge.common.component.OpenemsComponent;
    @@ -282,7 +282,7 @@ private void putValueIntoMap(Map values, Optional valueO
     	@Activate
     	void activate(ComponentContext context, Config config) {
     		debug("DischargeLimitConsideringCellVoltage.activate()");
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     
     		this.essId = config.ess_id();
     		this.batteryId = config.battery_id();
    diff --git a/io.openems.edge.controller.dischargelimitconsideringcellvoltage/test/io/openems/edge/controller/dischargelimitconsideringcellvoltage/TestController.java b/io.openems.edge.controller.dischargelimitconsideringcellvoltage/test/io/openems/edge/controller/dischargelimitconsideringcellvoltage/TestController.java
    index 6c4593f92c7..29ca4349a91 100644
    --- a/io.openems.edge.controller.dischargelimitconsideringcellvoltage/test/io/openems/edge/controller/dischargelimitconsideringcellvoltage/TestController.java
    +++ b/io.openems.edge.controller.dischargelimitconsideringcellvoltage/test/io/openems/edge/controller/dischargelimitconsideringcellvoltage/TestController.java
    @@ -184,6 +184,11 @@ public String id() {
     				return "";
     			}
     
    +			@Override
    +			public String alias() {
    +				return "";
    +			}
    +
     			@Override
     			public float firstCellVoltageLimit() {
     				return 2.85f;
    diff --git a/io.openems.edge.controller.ess.acisland/.classpath b/io.openems.edge.controller.ess.acisland/.classpath
    new file mode 100644
    index 00000000000..3ebd512b99a
    --- /dev/null
    +++ b/io.openems.edge.controller.ess.acisland/.classpath
    @@ -0,0 +1,12 @@
    +
    +
    +	
    +	
    +	
    +	
    +		
    +			
    +		
    +	
    +	
    +
    diff --git a/io.openems.edge.controller.ess.acisland/.gitignore b/io.openems.edge.controller.ess.acisland/.gitignore
    new file mode 100644
    index 00000000000..c2b941a96de
    --- /dev/null
    +++ b/io.openems.edge.controller.ess.acisland/.gitignore
    @@ -0,0 +1,2 @@
    +/bin_test/
    +/generated/
    diff --git a/io.openems.edge.controller.ess.acisland/.project b/io.openems.edge.controller.ess.acisland/.project
    new file mode 100644
    index 00000000000..fc820cd58d5
    --- /dev/null
    +++ b/io.openems.edge.controller.ess.acisland/.project
    @@ -0,0 +1,23 @@
    +
    +
    +	io.openems.edge.controller.ess.acisland
    +	
    +	
    +	
    +	
    +		
    +			org.eclipse.jdt.core.javabuilder
    +			
    +			
    +		
    +		
    +			bndtools.core.bndbuilder
    +			
    +			
    +		
    +	
    +	
    +		org.eclipse.jdt.core.javanature
    +		bndtools.core.bndnature
    +	
    +
    diff --git a/io.openems.edge.controller.ess.acisland/bnd.bnd b/io.openems.edge.controller.ess.acisland/bnd.bnd
    new file mode 100644
    index 00000000000..f3c868c6671
    --- /dev/null
    +++ b/io.openems.edge.controller.ess.acisland/bnd.bnd
    @@ -0,0 +1,22 @@
    +Bundle-Name: OpenEMS Edge Controller ESS AC-Island
    +Bundle-Vendor: FENECON GmbH
    +Bundle-License: https://opensource.org/licenses/EPL-2.0
    +Bundle-Version: 1.0.0.${tstamp}
    +Export-Package: io.openems.edge.controller.api
    +Private-Package: io.openems.edge.controller.ess.acisland
    +
    +-includeresource: {readme.md}
    +
    +-buildpath: \
    +	${buildpath},\
    +	io.openems.common;version=latest,\
    +	io.openems.edge.common;version=latest,\
    +	io.openems.edge.controller.api;version=latest,\
    +	io.openems.edge.ess.api;version=latest,\
    +	io.openems.edge.io.api;version=latest,\
    +	slf4j.api
    +
    +-testpath: ${testpath}
    +
    +javac.source: 1.8
    +javac.target: 1.8
    \ No newline at end of file
    diff --git a/edge/src/io/openems/impl/controller/acisland/Readme.md b/io.openems.edge.controller.ess.acisland/readme.md
    similarity index 100%
    rename from edge/src/io/openems/impl/controller/acisland/Readme.md
    rename to io.openems.edge.controller.ess.acisland/readme.md
    diff --git a/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/AcIsland.java b/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/AcIsland.java
    new file mode 100644
    index 00000000000..cbb3cd8276e
    --- /dev/null
    +++ b/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/AcIsland.java
    @@ -0,0 +1,251 @@
    +package io.openems.edge.controller.ess.acisland;
    +
    +import java.util.Optional;
    +
    +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.metatype.annotations.Designate;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +
    +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.types.ChannelAddress;
    +import io.openems.edge.common.channel.BooleanWriteChannel;
    +import io.openems.edge.common.channel.Doc;
    +import io.openems.edge.common.component.AbstractOpenemsComponent;
    +import io.openems.edge.common.component.ComponentManager;
    +import io.openems.edge.common.component.OpenemsComponent;
    +import io.openems.edge.common.sum.GridMode;
    +import io.openems.edge.controller.api.Controller;
    +import io.openems.edge.ess.api.SymmetricEss;
    +
    +@Designate(ocd = Config.class, factory = true)
    +@Component(name = "Controller.Ess.AcIsland", immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE)
    +public class AcIsland extends AbstractOpenemsComponent implements Controller, OpenemsComponent {
    +
    +	private final Logger log = LoggerFactory.getLogger(AcIsland.class);
    +
    +	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
    +		STATE_MACHINE(Doc.of(State.values()) //
    +				.text("Current State of State-Machine"));
    +
    +		private final Doc doc;
    +
    +		private ChannelId(Doc doc) {
    +			this.doc = doc;
    +		}
    +
    +		@Override
    +		public Doc doc() {
    +			return this.doc;
    +		}
    +	}
    +
    +	public AcIsland() {
    +		super(//
    +				OpenemsComponent.ChannelId.values(), //
    +				Controller.ChannelId.values(), //
    +				ChannelId.values() //
    +		);
    +	}
    +
    +	@Reference
    +	protected ComponentManager componentManager;
    +
    +	private Config config;
    +
    +	/**
    +	 * The current state in the State Machine
    +	 */
    +	private State state = State.UNDEFINED;
    +
    +	private ChannelAddress offGridOutputChannelAddress;
    +	private ChannelAddress onGridOutputChannelAddress;
    +
    +	private boolean isProducerDisconnected = false;
    +	private long timeProducerDisconnected;
    +
    +	@Activate
    +	void activate(ComponentContext context, Config config) throws OpenemsNamedException {
    +		super.activate(context, config.id(), config.alias(), config.enabled());
    +		this.config = config;
    +
    +		this.offGridOutputChannelAddress = ChannelAddress.fromString(config.offGridOutputChannelAddress());
    +		this.onGridOutputChannelAddress = ChannelAddress.fromString(config.onGridOutputChannelAddress());
    +	}
    +
    +	@Deactivate
    +	protected void deactivate() {
    +		super.deactivate();
    +	}
    +
    +	@Override
    +	public void run() throws OpenemsNamedException {
    +		// Get all required values - or abort with exception
    +		SymmetricEss ess = this.componentManager.getComponent(this.config.ess_id());
    +		GridMode gridMode = ess.getGridMode().value().asEnum();
    +		int soc = ess.getSoc().value().getOrError();
    +
    +		BooleanWriteChannel onGridOutputChannel = this.componentManager.getChannel(this.onGridOutputChannelAddress);
    +		BooleanWriteChannel offGridOutputChannel = this.componentManager.getChannel(this.offGridOutputChannelAddress);
    +		boolean isProducerOffGrid = this.isProducerOffGrid(onGridOutputChannel, offGridOutputChannel);
    +		boolean isProducerOff = this.isProducerOff(onGridOutputChannel, offGridOutputChannel);
    +		boolean isProducerOnGrid = this.isProducerOnGrid(onGridOutputChannel, offGridOutputChannel);
    +
    +		/*
    +		 * State Machine
    +		 */
    +		boolean stateChanged;
    +
    +		do {
    +			stateChanged = false;
    +			switch (this.state) {
    +			case OFF_GRID: {
    +				if (isProducerOffGrid || isProducerOff) {
    +					if (gridMode == GridMode.ON_GRID) {
    +						stateChanged = this.changeState(State.SWITCH_TO_ONGRID);
    +					} else {
    +						if (soc >= config.maxSoc()) {
    +							this.disconnectOffGrid(offGridOutputChannel);
    +						} else if (soc <= config.minSoc()) {
    +							this.connectOffGrid(offGridOutputChannel);
    +						}
    +					}
    +				} else {
    +					stateChanged = this.changeState(State.SWITCH_TO_OFFGRID);
    +				}
    +				break;
    +			}
    +
    +			case ON_GRID: {
    +				if (isProducerOnGrid) {
    +					if (gridMode == GridMode.OFF_GRID) {
    +						stateChanged = this.changeState(State.SWITCH_TO_OFFGRID);
    +					}
    +				} else {
    +					stateChanged = this.changeState(State.SWITCH_TO_ONGRID);
    +				}
    +				break;
    +			}
    +
    +			case SWITCH_TO_OFFGRID: {
    +				if (isProducerOff) {
    +					if (!this.isProducerDisconnected) {
    +						this.isProducerDisconnected = true;
    +						this.timeProducerDisconnected = System.currentTimeMillis();
    +					}
    +					if (this.timeProducerDisconnected + config.switchDelay() <= System.currentTimeMillis()
    +							&& this.isProducerDisconnected) {
    +						stateChanged = this.changeState(State.OFF_GRID);
    +					}
    +				} else {
    +					this.isProducerDisconnected = false;
    +					this.disconnectOnGrid(onGridOutputChannel);
    +					this.disconnectOffGrid(offGridOutputChannel);
    +				}
    +				break;
    +			}
    +
    +			case SWITCH_TO_ONGRID: {
    +				if (isProducerOnGrid) {
    +					this.changeState(State.ON_GRID);
    +					this.isProducerDisconnected = false;
    +				} else {
    +					if (isProducerOff) {
    +						if (!this.isProducerDisconnected) {
    +							this.isProducerDisconnected = true;
    +							this.timeProducerDisconnected = System.currentTimeMillis();
    +						}
    +						if (this.timeProducerDisconnected + config.switchDelay() <= System.currentTimeMillis()
    +								&& this.isProducerDisconnected) {
    +							this.connectOnGrid(onGridOutputChannel);
    +						}
    +					} else {
    +						this.isProducerDisconnected = false;
    +						this.disconnectOnGrid(onGridOutputChannel);
    +						this.disconnectOffGrid(offGridOutputChannel);
    +					}
    +				}
    +				break;
    +			}
    +
    +			default:
    +				if (gridMode == GridMode.ON_GRID) {
    +					stateChanged = this.changeState(State.SWITCH_TO_ONGRID);
    +				} else if (gridMode == GridMode.OFF_GRID) {
    +					stateChanged = this.changeState(State.SWITCH_TO_OFFGRID);
    +				}
    +			}
    +		} while (stateChanged); // execute again if the state changed
    +
    +		// store current state in StateMachine channel
    +		this.channel(ChannelId.STATE_MACHINE).setNextValue(this.state);
    +	}
    +
    +	private boolean isProducerOff(BooleanWriteChannel onGridOutputChannel, BooleanWriteChannel offGridOutputChannel) {
    +		return this.isOnGridOn(onGridOutputChannel) == false && this.isOffGridOn(offGridOutputChannel) == false;
    +	}
    +
    +	private boolean isProducerOffGrid(BooleanWriteChannel onGridOutputChannel,
    +			BooleanWriteChannel offGridOutputChannel) {
    +		return this.isOnGridOn(onGridOutputChannel) == false && this.isOffGridOn(offGridOutputChannel) == true;
    +	}
    +
    +	private boolean isProducerOnGrid(BooleanWriteChannel onGridOutputChannel,
    +			BooleanWriteChannel offGridOutputChannel) {
    +		return this.isOnGridOn(onGridOutputChannel) == true && this.isOffGridOn(offGridOutputChannel) == false;
    +	}
    +
    +	private void connectOnGrid(BooleanWriteChannel onGridOutputChannel) throws OpenemsNamedException {
    +		this.switchOutput(onGridOutputChannel, true, config.invertOnGridOutput());
    +	}
    +
    +	private void disconnectOnGrid(BooleanWriteChannel onGridOutputChannel) throws OpenemsNamedException {
    +		this.switchOutput(onGridOutputChannel, false, config.invertOnGridOutput());
    +	}
    +
    +	private boolean isOnGridOn(BooleanWriteChannel onGridOutputChannel) {
    +		return onGridOutputChannel.value().orElse(false) ^ this.config.invertOnGridOutput();
    +	}
    +
    +	private void connectOffGrid(BooleanWriteChannel offGridOutputChannel) throws OpenemsNamedException {
    +		this.switchOutput(offGridOutputChannel, true, config.invertOffGridOutput());
    +	}
    +
    +	private void disconnectOffGrid(BooleanWriteChannel offGridOutputChannel) throws OpenemsNamedException {
    +		this.switchOutput(offGridOutputChannel, false, config.invertOffGridOutput());
    +	}
    +
    +	private boolean isOffGridOn(BooleanWriteChannel offGridOutputChannel) {
    +		return offGridOutputChannel.value().orElse(false) ^ this.config.invertOffGridOutput();
    +	}
    +
    +	private void switchOutput(BooleanWriteChannel outputChannel, boolean on, boolean invertOutput)
    +			throws OpenemsNamedException {
    +		Optional currentValueOpt = outputChannel.value().asOptional();
    +		if (!currentValueOpt.isPresent() || currentValueOpt.get() != (on ^ invertOutput)) {
    +			this.logInfo(this.log,
    +					"Set output [" + outputChannel.address() + "] " + (on ^ invertOutput ? "ON" : "OFF") + ".");
    +			outputChannel.setNextWriteValue(on ^ invertOutput);
    +		}
    +	}
    +
    +	/**
    +	 * A flag to maintain change in the state
    +	 * 
    +	 * @param nextState the target state
    +	 * @return Flag that the state is changed or not
    +	 */
    +	private boolean changeState(State nextState) {
    +		if (this.state != nextState) {
    +			this.state = nextState;
    +			return true;
    +		} else
    +			return false;
    +	}
    +
    +}
    diff --git a/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/Config.java b/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/Config.java
    new file mode 100644
    index 00000000000..9f4d6befe64
    --- /dev/null
    +++ b/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/Config.java
    @@ -0,0 +1,46 @@
    +package io.openems.edge.controller.ess.acisland;
    +
    +import org.osgi.service.metatype.annotations.AttributeDefinition;
    +import org.osgi.service.metatype.annotations.ObjectClassDefinition;
    +
    +@ObjectClassDefinition(//
    +		name = "Controller ESS AC-Island", //
    +		description = "This controller sets a digital output channel according to the given value")
    +@interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
    +	String id() default "ctrlEssAcIsland0";
    +
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    +	String ess_id();
    +
    +	@AttributeDefinition(name = "Max-SoC", description = "If Off-Grid, the PV is disconnected at this SoC")
    +	int maxSoc() default 85;
    +
    +	@AttributeDefinition(name = "Min-SoC", description = "If Off-Grid, the PV is connected at this SoC")
    +	int minSoc() default 70;
    +
    +	@AttributeDefinition(name = "Switch-Dealy", description = "Time to wait in [ms] before switching the output on")
    +	int switchDelay() default 10_000;
    +
    +	@AttributeDefinition(name = "Invert On-Grid output", description = "True if the digital output for the On-Grid connector should be inverted.")
    +	boolean invertOnGridOutput() default false;
    +
    +	@AttributeDefinition(name = "Invert Off-Grid output", description = "True if the digital output for the Off-Grid connector should be inverted.")
    +	boolean invertOffGridOutput() default false;
    +
    +	@AttributeDefinition(name = "On-Grid Output Channel", description = "Channel address of the Digital Output for On-Grid connection")
    +	String onGridOutputChannelAddress();
    +
    +	@AttributeDefinition(name = "Off-Grid Output Channel", description = "Channel address of the Digital Output for Off-Grid connection")
    +	String offGridOutputChannelAddress();
    +
    +	String webconsole_configurationFactory_nameHint() default "Controller ESS AC-Island [{id}]";
    +
    +}
    \ No newline at end of file
    diff --git a/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/State.java b/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/State.java
    new file mode 100644
    index 00000000000..fbd6e67c9b6
    --- /dev/null
    +++ b/io.openems.edge.controller.ess.acisland/src/io/openems/edge/controller/ess/acisland/State.java
    @@ -0,0 +1,41 @@
    +package io.openems.edge.controller.ess.acisland;
    +
    +import io.openems.common.types.OptionsEnum;
    +
    +public enum State implements OptionsEnum {
    +	/**
    +	 * Unknown state on first start.
    +	 */
    +	UNDEFINED(-1, "Undefined"), //
    +
    +	OFF_GRID(1, "Off-Grid"), //
    +
    +	ON_GRID(2, "On-Grid"), //
    +
    +	SWITCH_TO_OFFGRID(3, "Switch to Off-Grid"), //
    +
    +	SWITCH_TO_ONGRID(4, "Switch to On-Grid");
    +
    +	private final int value;
    +	private final String name;
    +
    +	private State(int value, String name) {
    +		this.value = value;
    +		this.name = name;
    +	}
    +
    +	@Override
    +	public int getValue() {
    +		return value;
    +	}
    +
    +	@Override
    +	public String getName() {
    +		return name;
    +	}
    +
    +	@Override
    +	public OptionsEnum getUndefined() {
    +		return UNDEFINED;
    +	}
    +}
    \ No newline at end of file
    diff --git a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.scss b/io.openems.edge.controller.ess.acisland/test/.gitignore
    similarity index 100%
    rename from ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.scss
    rename to io.openems.edge.controller.ess.acisland/test/.gitignore
    diff --git a/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/Config.java b/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/Config.java
    index 3d75b1158fe..ecefd00674c 100644
    --- a/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/Config.java
    +++ b/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/Config.java
    @@ -8,8 +8,13 @@
     		description = "Limits total discharge for an Ess.")
     @interface Config {
     
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlLimitTotalDischarge0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/LimitTotalDischargeController.java b/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/LimitTotalDischargeController.java
    index 93f100b04cc..95562f21f64 100644
    --- a/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/LimitTotalDischargeController.java
    +++ b/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/LimitTotalDischargeController.java
    @@ -16,9 +16,9 @@
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     
    +import io.openems.common.channel.Level;
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.Level;
     import io.openems.edge.common.component.AbstractOpenemsComponent;
     import io.openems.edge.common.component.ComponentManager;
     import io.openems.edge.common.component.OpenemsComponent;
    @@ -87,7 +87,7 @@ protected LimitTotalDischargeController(Clock clock) {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     
     		this.essId = config.ess_id();
     		this.minSoc = config.minSoc();
    diff --git a/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/State.java b/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/State.java
    index dd851d9d96c..1e760838566 100644
    --- a/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/State.java
    +++ b/io.openems.edge.controller.ess.limittotaldischarge/src/io/openems/edge/controller/ess/limittotaldischarge/State.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.controller.ess.limittotaldischarge;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum State implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/Config.java b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/Config.java
    index ab2f13d4dcc..ded5c41eb44 100644
    --- a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/Config.java
    +++ b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Ess One Full Cycle", //
     		description = "Completes one full cycle for an Ess.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlOneFullCycle0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/CycleOrder.java b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/CycleOrder.java
    index 6349a15630b..c175089d9b1 100644
    --- a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/CycleOrder.java
    +++ b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/CycleOrder.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.controller.ess.onefullcycle;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum CycleOrder implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/EssOneFullCycle.java b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/EssOneFullCycle.java
    index 2d743170617..21c1ff485b1 100644
    --- a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/EssOneFullCycle.java
    +++ b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/EssOneFullCycle.java
    @@ -86,7 +86,7 @@ public EssOneFullCycle() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.power = Math.abs(config.power());
     		this.essId = config.ess_id();
     	}
    @@ -152,7 +152,7 @@ private void initializeEnums(ManagedSymmetricEss ess) throws InvalidValueExcepti
     	}
     
     	private void applyPower(ManagedSymmetricEss ess, int maxChargePower, int maxDischargePower)
    -			throws OpenemsException {
    +			throws OpenemsNamedException {
     		switch (this.state) {
     		case FIRST_CHARGE: {
     			/*
    diff --git a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/State.java b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/State.java
    index 5fe3043584a..c5727c0bde9 100644
    --- a/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/State.java
    +++ b/io.openems.edge.controller.ess.onefullcycle/src/io/openems/edge/controller/ess/onefullcycle/State.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.controller.ess.onefullcycle;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum State implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/Config.java b/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/Config.java
    index 20a0d5f4598..423e9c65dfc 100644
    --- a/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/Config.java
    +++ b/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/Config.java
    @@ -8,8 +8,13 @@
     		description = "Defines a fixed charge/discarge power to an Electric Vehicle Charging Station.")
     @interface Config {
     
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlEvcsFixActivePower0";
     
    +	@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 = "Evcs-id", description = "ID of Evcs device.")
    diff --git a/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/EvcsFixActivePower.java b/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/EvcsFixActivePower.java
    index d17d92345c5..9451d6d8af6 100644
    --- a/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/EvcsFixActivePower.java
    +++ b/io.openems.edge.controller.evcs.fixactivepower/src/io/openems/edge/controller/evcs/fixactivepower/EvcsFixActivePower.java
    @@ -62,7 +62,7 @@ protected EvcsFixActivePower(Clock clock) {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.evcs/bnd.bnd b/io.openems.edge.controller.evcs/bnd.bnd
    index 4979ea33f91..c3ceb7668fc 100644
    --- a/io.openems.edge.controller.evcs/bnd.bnd
    +++ b/io.openems.edge.controller.evcs/bnd.bnd
    @@ -3,18 +3,19 @@ Bundle-Vendor: FENECON GmbH
     Bundle-License: https://opensource.org/licenses/EPL-2.0
     Bundle-Version: 1.0.0.${tstamp}
     Export-Package: io.openems.edge.controller.api
    -Private-Package:  \
    -	io.openems.edge.controller.evcs
     
     -includeresource: {readme.md}
     
    --buildpath: ${buildpath},\
    +-buildpath: \
    +	${buildpath},\
     	io.openems.common;version=latest,\
     	io.openems.edge.common;version=latest,\
     	io.openems.edge.controller.api;version=latest,\
    -	io.openems.edge.evcs.api;version=latest
    +	io.openems.edge.evcs.api;version=latest,\
    +	io.openems.edge.evcs.keba.kecontact;version=latest
     
     -testpath: ${testpath}
     
     javac.source: 1.8
    -javac.target: 1.8
    \ No newline at end of file
    +javac.target: 1.8
    +-privatepackage: io.openems.edge.controller.evcs
    \ No newline at end of file
    diff --git a/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/ChargeMode.java b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/ChargeMode.java
    index 656a9dfcd95..1a8c10c8388 100644
    --- a/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/ChargeMode.java
    +++ b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/ChargeMode.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.controller.evcs;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum ChargeMode implements OptionsEnum {
     	FORCE_CHARGE(0, "Force-Charge"), //
    diff --git a/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/Config.java b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/Config.java
    index 158b4898c6d..4f00b95d15c 100644
    --- a/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/Config.java
    +++ b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/Config.java
    @@ -8,13 +8,21 @@
     		description = "Limits the maximum charging power of an electric vehicle charging station.")
     @interface Config {
     
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlEvcs0";
     
    +	@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 = "Evcs-ID", description = "ID of Evcs device.")
     	String evcs_id() default "evcs0";
     
    +	@AttributeDefinition(name = "Enabled charging", description = "Aktivates or deaktivates the Charging.")
    +	boolean enabledCharging() default true;
    +
     	@AttributeDefinition(name = "Charge-Mode", description = "Set the charge-mode.")
     	ChargeMode chargeMode() default ChargeMode.FORCE_CHARGE;
     
    @@ -24,6 +32,9 @@
     	@AttributeDefinition(name = "Default-charge minimum power [W]", description = "Set the minimum power for the default charge mod in Watt.")
     	int defaultChargeMinPower() default 0;
     
    +	@AttributeDefinition(name = "Priority of charging", description = "Decide which Component should be preferred.")
    +	Priority priority() default Priority.CAR;
    +
     	String webconsole_configurationFactory_nameHint() default "Controller Electric Vehicle Charging Station [{id}]";
     
     }
    \ No newline at end of file
    diff --git a/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/EvcsController.java b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/EvcsController.java
    index 53040545a7c..d5085a0b6ce 100644
    --- a/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/EvcsController.java
    +++ b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/EvcsController.java
    @@ -13,13 +13,17 @@
     import org.osgi.service.metatype.annotations.Designate;
     import org.slf4j.Logger;
     
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Unit;
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
     import io.openems.common.types.OpenemsType;
    +import io.openems.edge.common.channel.Channel;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.component.AbstractOpenemsComponent;
     import io.openems.edge.common.component.ComponentManager;
     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.sum.Sum;
     import io.openems.edge.controller.api.Controller;
     import io.openems.edge.evcs.api.Evcs;
    @@ -30,16 +34,18 @@
     		immediate = true, //
     		configurationPolicy = ConfigurationPolicy.REQUIRE //
     )
    -public class EvcsController extends AbstractOpenemsComponent implements Controller, OpenemsComponent {
    +public class EvcsController extends AbstractOpenemsComponent implements Controller, OpenemsComponent, ModbusSlave {
     
    -	private static final int RUN_EVERY_MINUTES = 1;
    +	private static final int RUN_EVERY_SECONDS = 5;
     
     	// private final Logger log = LoggerFactory.getLogger(EvcsController.class);
     	private final Clock clock;
     
    +	private boolean enabledCharging;
     	private int forceChargeMinPower = 0;
     	private int defaultChargeMinPower = 0;
     	private ChargeMode chargeMode;
    +	private Priority priority;
     	private String evcsId;
     	private LocalDateTime lastRun = LocalDateTime.MIN;
     
    @@ -60,7 +66,9 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
     				.unit(Unit.WATT).text("Minimum value for the force charge")),
     		DEFAULT_CHARGE_MINPOWER(Doc.of(OpenemsType.INTEGER) //
     				.unit(Unit.WATT) //
    -				.text("Minimum value for a default charge")); //
    +				.text("Minimum value for a default charge")),
    +		PRIORITY(Doc.of(Priority.values()).initialValue(Priority.CAR).text("Which component getting preferred")), //
    +		ENABLED_CHARGING(Doc.of(OpenemsType.BOOLEAN).text("Aktivates or deaktivates the Charging"));//
     
     		private final Doc doc;
     
    @@ -88,29 +96,27 @@ protected EvcsController(Clock clock) {
     	}
     
     	@Activate
    -	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +	void activate(ComponentContext context, Config config) throws OpenemsNamedException {
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     
    +		this.enabledCharging = config.enabledCharging();
     		this.forceChargeMinPower = Math.max(0, config.forceChargeMinPower()); // at least '0'
     		this.defaultChargeMinPower = Math.max(0, config.defaultChargeMinPower());
     		this.chargeMode = config.chargeMode();
    +		this.priority = config.priority();
    +		this.evcsId = config.evcs_id();
     
    -		switch (config.chargeMode()) {
    +		switch (chargeMode) {
     		case EXCESS_POWER:
     			this.channel(ChannelId.DEFAULT_CHARGE_MINPOWER).setNextValue(defaultChargeMinPower);
     			break;
     		case FORCE_CHARGE:
     			this.channel(ChannelId.FORCE_CHARGE_MINPOWER).setNextValue(forceChargeMinPower);
     			break;
    -
    -		}
    -		this.channel(ChannelId.CHARGE_MODE).setNextValue(config.chargeMode());
    -		this.evcsId = config.evcs_id();
    -
    -		// update filter for 'evcs'
    -		if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "evcs", config.evcs_id())) {
    -			return;
     		}
    +		this.channel(ChannelId.CHARGE_MODE).setNextValue(chargeMode);
    +		this.channel(ChannelId.PRIORITY).setNextValue(priority);
    +		this.channel(ChannelId.ENABLED_CHARGING).setNextValue(enabledCharging);
     	}
     
     	@Deactivate
    @@ -120,23 +126,42 @@ protected void deactivate() {
     
     	@Override
     	public void run() throws OpenemsNamedException {
    +		Evcs evcs = this.componentManager.getComponent(this.evcsId);
    +
    +		// Executes only if charging is enabled
    +		if (!this.enabledCharging) {
    +			evcs.setChargePower().setNextWriteValue(0);
    +			return;
    +		}
    +
     		// Execute only every ... minutes
    -		if (this.lastRun.plusMinutes(RUN_EVERY_MINUTES).isAfter(LocalDateTime.now(this.clock))) {
    +		if (this.lastRun.plusSeconds(RUN_EVERY_SECONDS).isAfter(LocalDateTime.now(this.clock))) {
     			return;
     		}
     
    -		Evcs evcs = this.componentManager.getComponent(this.evcsId);
    +		// Channel phases = evcs.channel("Phases");
     
     		int nextChargePower = 0;
     		int nextMinPower = 0;
     		switch (this.chargeMode) {
     		case EXCESS_POWER:
    -			int buyFromGrid = this.sum.getGridActivePower().value().orElse(0);
    -			int essDischarge = this.sum.getEssActivePower().value().orElse(0);
    -			int evcsCharge = evcs.getChargePower().value().orElse(0);
    -			nextChargePower = evcsCharge - buyFromGrid - essDischarge;
    -			if (nextChargePower < 1380 /* min 6A */ ) {
    -				nextChargePower = 0;
    +
    +			switch (priority) {
    +			case CAR:
    +
    +				nextChargePower = nextChargePower_PvMinusConsumtion();
    +				break;
    +
    +			case STORAGE:
    +
    +				int storageSoc = this.sum.getEssSoc().value().orElse(0);
    +
    +				if (storageSoc > 97) {
    +					nextChargePower = nextChargePower_PvMinusConsumtion();
    +				} else {
    +					nextChargePower = 0;
    +				}
    +				break;
     			}
     			nextMinPower = defaultChargeMinPower;
     			break;
    @@ -153,6 +178,49 @@ public void run() throws OpenemsNamedException {
     
     		// set charge power
     		evcs.setChargePower().setNextWriteValue(nextChargePower);
    +		lastRun = LocalDateTime.now();
    +	}
    +
    +	
    +	/* Only for testing
    +	 int[] exampleDataPV = { 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000,
    +			19000, 20000, 21000, 22000, 23000, 24000, 25000, 26000, 27000, 28000, 29000, 30000 };
    +	int consumption = 5000;
    +	int counter = 0;
    +	*/
    +
    +	/**
    +	 * Calculates the next charging power, depending on the current PV production
    +	 * and house consumption
    +	 * 
    +	 * @return
    +	 * @throws OpenemsNamedException
    +	 */
    +	private int nextChargePower_PvMinusConsumtion() throws OpenemsNamedException {
    +		int nextChargePower;
    +
    +		Evcs evcs = this.componentManager.getComponent(this.evcsId);
    +
    +		int buyFromGrid = this.sum.getGridActivePower().value().orElse(0);
    +		int essDischarge = this.sum.getEssActivePower().value().orElse(0);
    +		int evcsCharge = evcs.getChargePower().value().orElse(0);
    +		
    +
    +		/* Only for testing
    +		 
    +		 if (counter > exampleDataPV.length - 1) { counter = 0; } 
    +		 buyFromGrid = 0;
    +		 essDischarge = (consumption + evcsCharge) - exampleDataPV[counter]; 
    +		 counter++;
    +		 */
    +		
    +		nextChargePower = evcsCharge - buyFromGrid - essDischarge;
    +
    +		Channel minChannel = evcs.channel(Evcs.ChannelId.MINIMUM_POWER);
    +		if (nextChargePower < minChannel.value().orElse(0)) { /* charging under 6A isn't possible */
    +			nextChargePower = 0;
    +		}
    +		return nextChargePower;
     	}
     
     	@Override
    @@ -165,4 +233,8 @@ protected void logInfo(Logger log, String message) {
     		super.logInfo(log, message);
     	}
     
    +	@Override
    +	public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
    +		return new ModbusSlaveTable(OpenemsComponent.getModbusSlaveNatureTable(accessMode));
    +	}
     }
    diff --git a/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/Priority.java b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/Priority.java
    new file mode 100644
    index 00000000000..f76ae23a0e3
    --- /dev/null
    +++ b/io.openems.edge.controller.evcs/src/io/openems/edge/controller/evcs/Priority.java
    @@ -0,0 +1,41 @@
    +package io.openems.edge.controller.evcs;
    +
    +import io.openems.common.types.OptionsEnum;
    +
    +/**
    + * The Priorities for charging.
    + * Which Component should be preferred
    + * 
    + * Todo: If more than 2 Apps are installed, the Priority of the Apps should be handled in an extra controller.
    + */
    +public enum Priority implements io.openems.common.types.OptionsEnum{
    +	
    +	CAR(0, "Car"),
    +	STORAGE(1, "Storage");
    +
    +	private final int value;
    +	private final String name;
    +
    +	private Priority(int value, String name) {
    +		this.value = value;
    +		this.name = name;
    +	}
    +	
    +	@Override
    +	public int getValue() {
    +		return value;
    +	}
    +
    +	@Override
    +	public String getName() {
    +		return name;
    +	}
    +
    +	@Override
    +	public OptionsEnum getUndefined() {
    +		return CAR;
    +	}
    +	
    +	
    +	
    +}
    diff --git a/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/Config.java b/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/Config.java
    index 266a6803976..79fbf3acc59 100644
    --- a/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/Config.java
    +++ b/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller High-Load Timeslot", //
     		description = "This controller discharges the storage system at a defined time with a defined load; charges within remaining time.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlHighLoadTimeslot0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/HighLoadTimeslot.java b/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/HighLoadTimeslot.java
    index 5f015126bfd..9aa4fcc0459 100644
    --- a/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/HighLoadTimeslot.java
    +++ b/io.openems.edge.controller.highloadtimeslot/src/io/openems/edge/controller/highloadtimeslot/HighLoadTimeslot.java
    @@ -98,7 +98,7 @@ void activate(ComponentContext context, Config config) throws OpenemsException {
     		this.hysteresisSoc = config.hysteresisSoc();
     		this.weekdayDayFilter = config.weekdayFilter();
     
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.controller.io.alarm/.classpath b/io.openems.edge.controller.io.alarm/.classpath
    new file mode 100644
    index 00000000000..3ebd512b99a
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/.classpath
    @@ -0,0 +1,12 @@
    +
    +
    +	
    +	
    +	
    +	
    +		
    +			
    +		
    +	
    +	
    +
    diff --git a/io.openems.edge.controller.io.alarm/.gitignore b/io.openems.edge.controller.io.alarm/.gitignore
    new file mode 100644
    index 00000000000..c2b941a96de
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/.gitignore
    @@ -0,0 +1,2 @@
    +/bin_test/
    +/generated/
    diff --git a/io.openems.edge.controller.io.alarm/.project b/io.openems.edge.controller.io.alarm/.project
    new file mode 100644
    index 00000000000..a56093959ef
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/.project
    @@ -0,0 +1,23 @@
    +
    +
    +	io.openems.edge.controller.io.alarm
    +	
    +	
    +	
    +	
    +		
    +			org.eclipse.jdt.core.javabuilder
    +			
    +			
    +		
    +		
    +			bndtools.core.bndbuilder
    +			
    +			
    +		
    +	
    +	
    +		org.eclipse.jdt.core.javanature
    +		bndtools.core.bndnature
    +	
    +
    diff --git a/io.openems.edge.controller.io.alarm/bnd.bnd b/io.openems.edge.controller.io.alarm/bnd.bnd
    new file mode 100644
    index 00000000000..b80f34558d9
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/bnd.bnd
    @@ -0,0 +1,28 @@
    +Bundle-Name: OpenEMS Edge Controller IOAlarm
    +Bundle-License: https://opensource.org/licenses/EPL-2.0
    +Bundle-Vendor: FENECON GmbH
    +Bundle-Version:	1.0.0.${tstamp}
    +Bundle-Description: The provider bundle for io.openems.edge.controller.io.alarm. This Controller \
    +	can be used to describe the alarms in multiple different levels.
    +
    +Export-Package:  io.openems.edge.controller.api
    +	
    +Private-Package:  \
    +	io.openems.edge.controller.io.alarm
    +
    +-includeresource: {readme.md}
    +
    +-buildpath: \
    +	${buildpath},\
    +	io.openems.edge.controller.api;version=latest,\
    +	io.openems.common;version=latest,\
    +	io.openems.edge.common;version=latest,\
    +	io.openems.edge.ess.api;version=latest,\
    +	io.openems.edge.io.api;version=latest,\
    +	com.google.guava,\
    +	com.google.gson
    +
    +-testpath: ${testpath}
    +
    +javac.source: 1.8
    +javac.target: 1.8
    \ No newline at end of file
    diff --git a/io.openems.edge.controller.io.alarm/readme.md b/io.openems.edge.controller.io.alarm/readme.md
    new file mode 100644
    index 00000000000..306c1e11050
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/readme.md
    @@ -0,0 +1,8 @@
    +# io.openems.edge.controller.io.alarm Provider
    +
    +${Bundle-Description}
    +
    +## Example
    +
    +## References
    +
    diff --git a/io.openems.edge.controller.io.alarm/src/io/openems/edge/controller/io/alarm/Config.java b/io.openems.edge.controller.io.alarm/src/io/openems/edge/controller/io/alarm/Config.java
    new file mode 100644
    index 00000000000..11f40e9f499
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/src/io/openems/edge/controller/io/alarm/Config.java
    @@ -0,0 +1,28 @@
    +package io.openems.edge.controller.io.alarm;
    +
    +import org.osgi.service.metatype.annotations.AttributeDefinition;
    +import org.osgi.service.metatype.annotations.ObjectClassDefinition;
    +
    +@ObjectClassDefinition(//
    +		name = "Controller IO Alarm", //
    +		description = "Switches a digital output, when one or more State-Channels are set. Can be used to signal alarms.")
    +@interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
    +	String id() default "ctrlIoAlarm0";
    +
    +	@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 = "Input Channels", description = "Addresses of the input State-Channels")
    +	String[] inputChannelAddress();
    +
    +	@AttributeDefinition(name = "Output Channel", description = "Channel address of the Digital Output that should be switched")
    +	String outputChannelAddress();
    +
    +	String webconsole_configurationFactory_nameHint() default "Controller IO Alarm [{id}]";
    +
    +}
    diff --git a/io.openems.edge.controller.io.alarm/src/io/openems/edge/controller/io/alarm/IoAlarm.java b/io.openems.edge.controller.io.alarm/src/io/openems/edge/controller/io/alarm/IoAlarm.java
    new file mode 100644
    index 00000000000..0f721a6b6f0
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/src/io/openems/edge/controller/io/alarm/IoAlarm.java
    @@ -0,0 +1,99 @@
    +package io.openems.edge.controller.io.alarm;
    +
    +import java.util.Optional;
    +
    +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.metatype.annotations.Designate;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +
    +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.types.ChannelAddress;
    +import io.openems.common.types.OpenemsType;
    +import io.openems.edge.common.channel.Doc;
    +import io.openems.edge.common.channel.StateChannel;
    +import io.openems.edge.common.channel.WriteChannel;
    +import io.openems.edge.common.component.AbstractOpenemsComponent;
    +import io.openems.edge.common.component.ComponentManager;
    +import io.openems.edge.common.component.OpenemsComponent;
    +import io.openems.edge.common.type.TypeUtils;
    +import io.openems.edge.controller.api.Controller;
    +
    +@Designate(ocd = Config.class, factory = true)
    +@Component(//
    +		name = "Controller.IO.Alarm", //
    +		immediate = true, //
    +		configurationPolicy = ConfigurationPolicy.REQUIRE)
    +public class IoAlarm extends AbstractOpenemsComponent implements Controller, OpenemsComponent {
    +
    +	private final Logger log = LoggerFactory.getLogger(IoAlarm.class);
    +
    +	@Reference
    +	protected ComponentManager componentManager;
    +
    +	private Config config;
    +
    +	public IoAlarm() {
    +		super(//
    +				OpenemsComponent.ChannelId.values(), //
    +				Controller.ChannelId.values(), //
    +				ChannelId.values() //
    +		);
    +	}
    +
    +	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;
    +		}
    +	}
    +
    +	@Activate
    +	void activate(ComponentContext context, Config config) throws OpenemsNamedException {
    +		super.activate(context, config.id(), config.alias(), config.enabled());
    +		this.config = config;
    +	}
    +
    +	@Deactivate
    +	protected void deactivate() {
    +		super.deactivate();
    +	}
    +
    +	@Override
    +	public void run() throws IllegalArgumentException, OpenemsNamedException {
    +		boolean setOutput = false;
    +
    +		for (String channelAddress : this.config.inputChannelAddress()) {
    +			StateChannel channel = this.componentManager.getChannel(ChannelAddress.fromString(channelAddress));
    +			// Reading the value of all input channels
    +			boolean isStateChannelSet = TypeUtils.getAsType(OpenemsType.BOOLEAN, channel.value().getOrError());
    +
    +			if (isStateChannelSet) {
    +				// If Channel was set: signal true
    +				setOutput = true;
    +				break;
    +			}
    +		}
    +
    +		// Set Output Channel
    +		WriteChannel outputChannel = this.componentManager
    +				.getChannel(ChannelAddress.fromString(this.config.outputChannelAddress()));
    +		Optional currentValueOpt = outputChannel.value().asOptional();
    +		if (!currentValueOpt.isPresent() || currentValueOpt.get() != setOutput) {
    +			this.logInfo(this.log, "Set output [" + outputChannel.address() + "] " + setOutput + ".");
    +			outputChannel.setNextWriteValue(setOutput);
    +		}
    +	}
    +}
    diff --git a/io.openems.edge.controller.io.alarm/test/.gitignore b/io.openems.edge.controller.io.alarm/test/.gitignore
    new file mode 100644
    index 00000000000..e69de29bb2d
    diff --git a/io.openems.edge.controller.io.alarm/test/io/openems/edge/controller/io/alarm/DummyComponent.java b/io.openems.edge.controller.io.alarm/test/io/openems/edge/controller/io/alarm/DummyComponent.java
    new file mode 100644
    index 00000000000..0773a116185
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/test/io/openems/edge/controller/io/alarm/DummyComponent.java
    @@ -0,0 +1,40 @@
    +package io.openems.edge.controller.io.alarm;
    +
    +import io.openems.edge.common.channel.Doc;
    +import io.openems.common.channel.Level;
    +import io.openems.edge.common.component.AbstractOpenemsComponent;
    +import io.openems.edge.common.component.OpenemsComponent;
    +
    +/**
    + * Provides a simple OpenEMS Component which can be used for testing.
    + */
    +public class DummyComponent extends AbstractOpenemsComponent implements OpenemsComponent {
    +
    +	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
    +		/**
    +		 * Dummy state channels for testing.
    +		 */
    +		STATE_0(Doc.of(Level.WARNING).text("State 0")),
    +		STATE_1(Doc.of(Level.WARNING).text("State 1")),
    +		;
    +
    +		private final Doc doc;
    +
    +		private ChannelId(Doc doc) {
    +			this.doc = doc;
    +		}
    +
    +		public Doc doc() {	
    +			return this.doc;
    +		}
    +	}
    +	
    +	public DummyComponent(String id) {
    +		super(//
    +				OpenemsComponent.ChannelId.values(),//
    +				ChannelId.values() //
    +		);
    +		super.activate(null, id, "", true);
    +	}
    +
    +}
    diff --git a/io.openems.edge.controller.io.alarm/test/io/openems/edge/controller/io/alarm/IoAlarmTest.java b/io.openems.edge.controller.io.alarm/test/io/openems/edge/controller/io/alarm/IoAlarmTest.java
    new file mode 100644
    index 00000000000..8f87db1aaae
    --- /dev/null
    +++ b/io.openems.edge.controller.io.alarm/test/io/openems/edge/controller/io/alarm/IoAlarmTest.java
    @@ -0,0 +1,95 @@
    +package io.openems.edge.controller.io.alarm;
    +
    +import java.util.ArrayList;
    +
    +import org.junit.Test;
    +
    +import io.openems.common.types.ChannelAddress;
    +import io.openems.edge.common.test.AbstractComponentConfig;
    +import io.openems.edge.common.test.AbstractComponentTest.TestCase;
    +import io.openems.edge.common.test.DummyComponentManager;
    +import io.openems.edge.controller.test.ControllerTest;
    +import io.openems.edge.io.test.DummyInputOutput;
    +
    +public class IoAlarmTest {
    +
    +	@SuppressWarnings("all")
    +	private static class MyConfig extends AbstractComponentConfig implements Config {
    +
    +		private final String[] inputChannelAddress;
    +		private final String outputChannelAddress;
    +
    +		public MyConfig(String id, String[] inputChannelAddress, String outputChannelAddress) {
    +
    +			super(Config.class, id);
    +			this.inputChannelAddress = inputChannelAddress;
    +			this.outputChannelAddress = outputChannelAddress;
    +
    +		}
    +
    +		@Override
    +		public String[] inputChannelAddress() {
    +			return this.inputChannelAddress;
    +		}
    +
    +		@Override
    +		public String outputChannelAddress() {
    +			return this.outputChannelAddress;
    +		}
    +
    +	}
    +
    +	@Test
    +	public void test() throws Exception {
    +		// initialize the controller
    +		IoAlarm controller = new IoAlarm();
    +		// Add referenced services
    +		DummyComponentManager componentManager = new DummyComponentManager();
    +		controller.componentManager = componentManager;
    +
    +		ArrayList channelAddress = new ArrayList();		
    +		
    +		ChannelAddress ess0State0 = new ChannelAddress("ess0", "State0");		
    +		ChannelAddress ess0State1 = new ChannelAddress("ess0", "State1");			
    +		
    +		channelAddress.add(ess0State0);		
    +		channelAddress.add(ess0State1);				
    +		
    +		ChannelAddress output0 = new ChannelAddress("io0", "InputOutput0");
    +				
    +		String[] inAddress = new String[channelAddress.size()];
    +
    +		for (int i = 0; i < channelAddress.size(); i++) {
    +			inAddress[i] = channelAddress.get(i).toString();
    +		}
    +		
    +		MyConfig myconfig = new MyConfig("ctrl1", inAddress, output0.toString());
    +
    +		controller.activate(null, myconfig);
    +		controller.activate(null, myconfig);		
    +		
    +		DummyComponent ess0 = new DummyComponent("ess0");
    +		DummyInputOutput io = new DummyInputOutput("io0");
    +
    +		new ControllerTest(controller, componentManager, ess0, io)//
    +				.next(new TestCase() //
    +						.input(ess0State0, true) //
    +						.input(ess0State1, false) //
    +						.output(output0, true)) //
    +				.next(new TestCase() //
    +						.input(ess0State0, false) //
    +						.input(ess0State1, true) //
    +						.output(output0, true)) //
    +				.next(new TestCase()
    +						.input(ess0State0, true) //
    +				        .input(ess0State1, true) //
    +				        .output(output0, true))
    +				.next(new TestCase()
    +						.input(ess0State0, false) //
    +				        .input(ess0State1, false) //
    +				        .output(output0, false))
    +				.run();
    +
    +	}
    +
    +}
    diff --git a/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/Config.java b/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/Config.java
    index b89ff2798a0..01cceb069ba 100644
    --- a/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/Config.java
    +++ b/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/Config.java
    @@ -8,8 +8,13 @@
     		description = "This controller sets a digital output channel according to the given value")
     @interface Config {
     
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlIoFixDigitalOutput0";
     
    +	@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 = "Output Channel", description = "Channel address of the Digital Output that should be switched")
    diff --git a/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/FixDigitalOutput.java b/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/FixDigitalOutput.java
    index 0d5a1ead802..4b8353a2e13 100644
    --- a/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/FixDigitalOutput.java
    +++ b/io.openems.edge.controller.io.fixdigitaloutput/src/io/openems/edge/controller/io/fixdigitaloutput/FixDigitalOutput.java
    @@ -73,7 +73,7 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept
     		this.isOn = config.isOn();
     		this.outputChannelAddress = ChannelAddress.fromString(config.outputChannelAddress());
     
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/Config.java b/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/Config.java
    index f175ffa9082..9ac33c10731 100644
    --- a/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/Config.java
    +++ b/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller PV-Inverter Fix Power Limit", //
     		description = "Defines a fixed power limitation to PV inverter.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlPvInverterFixPowerLimit0";
     
    +	@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 = "PV-Inverter-ID", description = "ID of PV-Inverter device.")
    diff --git a/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/PvInverterFixPowerLimit.java b/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/PvInverterFixPowerLimit.java
    index 259a355aac6..e6d8296a6e8 100644
    --- a/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/PvInverterFixPowerLimit.java
    +++ b/io.openems.edge.controller.pvinverter.fixpowerlimit/src/io/openems/edge/controller/pvinverter/fixpowerlimit/PvInverterFixPowerLimit.java
    @@ -57,7 +57,7 @@ public PvInverterFixPowerLimit() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     
     		this.pvInverterId = config.pvInverter_id();
     		this.powerLimit = config.powerLimit();
    diff --git a/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Balancing.java b/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Balancing.java
    index c5a046e11c0..89ff34ecf92 100644
    --- a/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Balancing.java
    +++ b/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Balancing.java
    @@ -26,6 +26,8 @@
     @Component(name = "Controller.Symmetric.Balancing", immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE)
     public class Balancing extends AbstractOpenemsComponent implements Controller, OpenemsComponent {
     
    +	public final static double DEFAULT_MAX_ADJUSTMENT_RATE = 0.2;
    +
     	private final Logger log = LoggerFactory.getLogger(Balancing.class);
     
     	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
    @@ -55,9 +57,11 @@ public Balancing() {
     
     	private Config config;
     
    +	private int lastSetActivePower = 0;
    +
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    @@ -75,7 +79,8 @@ protected void deactivate() {
     	 */
     	private int calculateRequiredPower(ManagedSymmetricEss ess, SymmetricMeter meter) {
     		return meter.getActivePower().value().orElse(0) /* current buy-from/sell-to grid */
    -				+ ess.getActivePower().value().orElse(0) /* current charge/discharge Ess */;
    +				+ ess.getActivePower().value().orElse(0) /* current charge/discharge Ess */
    +				- config.targetGridSetpoint(); /* the configured target setpoint */
     	}
     
     	@Override
    @@ -90,18 +95,42 @@ public void run() throws OpenemsNamedException {
     		if (gridMode.isUndefined()) {
     			this.logWarn(this.log, "Grid-Mode is [UNDEFINED]");
     		}
    -		if (gridMode != GridMode.ON_GRID) {
    +		switch (gridMode) {
    +		case ON_GRID:
    +		case UNDEFINED:
    +			break;
    +		case OFF_GRID:
     			return;
     		}
    +
     		/*
     		 * Calculates required charge/discharge power
     		 */
     		int calculatedPower = this.calculateRequiredPower(ess, meter);
     
    +		if (Math.abs(this.lastSetActivePower) > 100 && Math.abs(calculatedPower) > 100
    +				&& Math.abs(this.lastSetActivePower - calculatedPower) > (Math.abs(this.lastSetActivePower)
    +						* this.config.maxPowerAdjustmentRate())) {
    +			if (this.lastSetActivePower > calculatedPower) {
    +				int newPower = this.lastSetActivePower
    +						- (int) Math.abs(this.lastSetActivePower * this.config.maxPowerAdjustmentRate());
    +				this.logInfo(log, "Adjust [-] Last [" + this.lastSetActivePower + "] Calculated [" + calculatedPower
    +						+ "] Corrected to [" + newPower + "]");
    +				calculatedPower = newPower;
    +			} else {
    +				int newPower = this.lastSetActivePower
    +						+ (int) Math.abs(this.lastSetActivePower * this.config.maxPowerAdjustmentRate());
    +				this.logInfo(log, "Adjust [+] Last [" + this.lastSetActivePower + "] Calculated [" + calculatedPower
    +						+ "] Corrected to [" + newPower + "]");
    +				calculatedPower = newPower;
    +			}
    +		}
    +
     		// adjust value so that it fits into Min/MaxActivePower
     		calculatedPower = ess.getPower().fitValueIntoMinMaxPower(ess, Phase.ALL, Pwr.ACTIVE, calculatedPower);
    -		// TODO this should not be anymore required, as it is done within
    -		// ManagedSymmetricEss.SET_ACTIVE_POWER_EQUALS Channel
    +
    +		// store lastSetActivePower
    +		this.lastSetActivePower = calculatedPower;
     
     		/*
     		 * set result
    diff --git a/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Config.java b/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Config.java
    index de5fd6a73ea..c946461393b 100644
    --- a/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Config.java
    +++ b/io.openems.edge.controller.symmetric.balancing/src/io/openems/edge/controller/symmetric/balancing/Config.java
    @@ -8,8 +8,13 @@
     		description = "Optimizes the self-consumption by keeping the grid meter on zero.")
     @interface Config {
     
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlBalancing0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    @@ -18,6 +23,12 @@
     	@AttributeDefinition(name = "Grid-Meter-ID", description = "ID of the Grid-Meter.")
     	String meter_id();
     
    +	@AttributeDefinition(name = "Target Grid Setpoint", description = "The target setpoint for grid. Positive for buy-from-grid; negative for sell-to-grid.")
    +	int targetGridSetpoint() default 0;
    +
    +	@AttributeDefinition(name = "Max power adjustment rate", description = "The maximum rate of power adjustments within one Cycle.")
    +	double maxPowerAdjustmentRate() default Balancing.DEFAULT_MAX_ADJUSTMENT_RATE;
    +
     	String webconsole_configurationFactory_nameHint() default "Controller Balancing Symmetric [{id}]";
     
     }
    \ No newline at end of file
    diff --git a/io.openems.edge.controller.symmetric.balancing/test/io/openems/edge/controller/symmetric/balancing/BalancingTest.java b/io.openems.edge.controller.symmetric.balancing/test/io/openems/edge/controller/symmetric/balancing/BalancingTest.java
    index 10a13162e1e..96df9f39d60 100644
    --- a/io.openems.edge.controller.symmetric.balancing/test/io/openems/edge/controller/symmetric/balancing/BalancingTest.java
    +++ b/io.openems.edge.controller.symmetric.balancing/test/io/openems/edge/controller/symmetric/balancing/BalancingTest.java
    @@ -36,6 +36,16 @@ public String ess_id() {
     		public String meter_id() {
     			return this.meterId;
     		}
    +
    +		@Override
    +		public double maxPowerAdjustmentRate() {
    +			return Balancing.DEFAULT_MAX_ADJUSTMENT_RATE;
    +		}
    +
    +		@Override
    +		public int targetGridSetpoint() {
    +			return 0;
    +		}
     	}
     
     	@Test
    @@ -62,6 +72,9 @@ public void test() throws Exception {
     						.input(ess0GridMode, GridMode.ON_GRID) //
     						.input(ess0ActivePower, 1000).input(meter0ActivePower, 2000) //
     						.output(ess0SetActivePowerEquals, 3000)) //
    +				.next(new TestCase() //
    +						.input(ess0ActivePower, 1500).input(meter0ActivePower, 2500) //
    +						.output(ess0SetActivePowerEquals, 3600)) //
     				.next(new TestCase() //
     						.input(ess0ActivePower, 1500).input(meter0ActivePower, 2500) //
     						.output(ess0SetActivePowerEquals, 4000)) //
    diff --git a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/BalancingSchedule.java b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/BalancingSchedule.java
    index d864dc02214..288a2f4fe24 100644
    --- a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/BalancingSchedule.java
    +++ b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/BalancingSchedule.java
    @@ -82,7 +82,7 @@ public BalancingSchedule() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		// update filter for 'ess'
     		if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "ess", config.ess_id())) {
     			return;
    diff --git a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/Config.java b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/Config.java
    index 4887bef6ae9..768c04c7f60 100644
    --- a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/Config.java
    +++ b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Balancing Schedule Symmetric", //
     		description = "Controls an ESS with the target to keep the grid-meter on a value defined in a Schedule.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlBalancingSchedule0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/Config.java b/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/Config.java
    index b52b6ff9f1d..d7767382c3f 100644
    --- a/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/Config.java
    +++ b/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Fix Active Power Symmetric", //
     		description = "Defines a fixed charge/discharge power to a symmetric energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlFixActivePower0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/SymmetricFixActivePower.java b/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/SymmetricFixActivePower.java
    index 63ef904f7bc..cd53bdf09ee 100644
    --- a/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/SymmetricFixActivePower.java
    +++ b/io.openems.edge.controller.symmetric.fixactivepower/src/io/openems/edge/controller/symmetric/fixactivepower/SymmetricFixActivePower.java
    @@ -55,7 +55,7 @@ public SymmetricFixActivePower() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/Config.java b/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/Config.java
    index 0b21ddce335..8f1b2b3ed85 100644
    --- a/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/Config.java
    +++ b/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Fix Reactive Power Symmetric", //
     		description = "Defines a fixed reactive power to a symmetric energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlFixReactivePower0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/SymmetricFixReactivePower.java b/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/SymmetricFixReactivePower.java
    index 638868b9c0c..ff33d9183d7 100644
    --- a/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/SymmetricFixReactivePower.java
    +++ b/io.openems.edge.controller.symmetric.fixreactivepower/src/io/openems/edge/controller/symmetric/fixreactivepower/SymmetricFixReactivePower.java
    @@ -55,7 +55,7 @@ public SymmetricFixReactivePower() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/Config.java b/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/Config.java
    index 60428526ee8..dbcdc05b623 100644
    --- a/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/Config.java
    +++ b/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Limit Active Power Symmetric", //
     		description = "Defines charge and discharge limits for a symmetric energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlLimitActivePower0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    @@ -16,7 +22,7 @@
     
     	@AttributeDefinition(name = "Max Charge Power [W]", description = "Positive value describing the maximum Charge Power.")
     	int maxChargePower();
    -	
    +
     	@AttributeDefinition(name = "Max Discharge Power [W]", description = "Positive value describing the maximum Discharge Power.")
     	int maxDischargePower();
     
    diff --git a/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/SymmetricLimitActivePower.java b/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/SymmetricLimitActivePower.java
    index f2416c1ed82..82cde314beb 100644
    --- a/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/SymmetricLimitActivePower.java
    +++ b/io.openems.edge.controller.symmetric.limitactivepower/src/io/openems/edge/controller/symmetric/limitactivepower/SymmetricLimitActivePower.java
    @@ -68,7 +68,7 @@ public SymmetricLimitActivePower() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.essId = config.ess_id();
     		this.maxChargePower = config.maxChargePower() * -1;
     		this.maxDischargePower = config.maxDischargePower();
    diff --git a/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/Config.java b/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/Config.java
    index 266384e4519..d4c1562c421 100644
    --- a/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/Config.java
    +++ b/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Linear Power Band Symmetric", //
     		description = "Defines a fixed max/min power to a symmetric energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlLinearPowerBand0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/SymmetricLinearPowerBand.java b/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/SymmetricLinearPowerBand.java
    index a2f697b6ecc..3b6ebc68bb5 100644
    --- a/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/SymmetricLinearPowerBand.java
    +++ b/io.openems.edge.controller.symmetric.linearpowerband/src/io/openems/edge/controller/symmetric/linearpowerband/SymmetricLinearPowerBand.java
    @@ -32,7 +32,7 @@ public class SymmetricLinearPowerBand extends AbstractOpenemsComponent implement
     	private Config config;
     	private int currentPower = 0;
     	private State state = State.DOWNWARDS;
    -	
    +
     	private enum State {
     		DOWNWARDS, UPWARDS
     	}
    @@ -61,7 +61,7 @@ public SymmetricLinearPowerBand() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java
    index 1d6983240f5..b68a602212a 100644
    --- a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java
    +++ b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Peak-Shaving Symmetric", //
     		description = "Cuts power peaks and recharges the battery in low consumption periods.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlPeakShaving0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/PeakShaving.java b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/PeakShaving.java
    index 620654a8d58..83110c2ad5a 100644
    --- a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/PeakShaving.java
    +++ b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/PeakShaving.java
    @@ -63,7 +63,7 @@ public PeakShaving() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/Config.java b/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/Config.java
    index 3a9fd963840..776f9d59813 100644
    --- a/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/Config.java
    +++ b/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/Config.java
    @@ -7,8 +7,14 @@
     		name = "Controller Random Power Symmetric", //
     		description = "Defines a random power within fixed max/min power to a symmetric energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlRandomPower0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/SymmetricRandomPower.java b/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/SymmetricRandomPower.java
    index 8a0194e52e2..52784427b29 100644
    --- a/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/SymmetricRandomPower.java
    +++ b/io.openems.edge.controller.symmetric.randompower/src/io/openems/edge/controller/symmetric/randompower/SymmetricRandomPower.java
    @@ -57,7 +57,7 @@ public SymmetricRandomPower() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		this.config = config;
     	}
     
    diff --git a/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/Config.java b/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/Config.java
    index 36e782e525d..d4b5410f308 100644
    --- a/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/Config.java
    +++ b/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/Config.java
    @@ -8,8 +8,13 @@
     		description = "Defines a reactive power voltage characteristic for storage system.")
     @interface Config {
     
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ctrlRctvPwrVltgChrctrstc0";
     
    +	@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 = "Ess-ID", description = "ID of Ess device.")
    diff --git a/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/ReactivePowerVoltageCharacteristic.java b/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/ReactivePowerVoltageCharacteristic.java
    index 3e0b9dbf257..ae14ff2e73c 100644
    --- a/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/ReactivePowerVoltageCharacteristic.java
    +++ b/io.openems.edge.controller.symmetric.reactivepowervoltagecharacteristic/src/io/openems/edge/controller/symmetric/reactivepowervoltagecharacteristic/ReactivePowerVoltageCharacteristic.java
    @@ -84,7 +84,7 @@ public ReactivePowerVoltageCharacteristic() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) throws OpenemsNamedException {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     		if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "ess", config.ess_id())) {
     			return;
     		}
    diff --git a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java
    index 5f83d59205a..8f06bccff6b 100644
    --- a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java
    +++ b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java
    @@ -7,9 +7,12 @@
     import java.util.Collections;
     import java.util.Dictionary;
     import java.util.Enumeration;
    +import java.util.HashMap;
     import java.util.HashSet;
     import java.util.Hashtable;
     import java.util.List;
    +import java.util.Map;
    +import java.util.Map.Entry;
     import java.util.Set;
     import java.util.TreeMap;
     import java.util.concurrent.CompletableFuture;
    @@ -48,8 +51,10 @@
     import org.xml.sax.SAXException;
     
     import com.google.gson.JsonElement;
    +import com.google.gson.JsonPrimitive;
     
     import io.openems.common.OpenemsConstants;
    +import io.openems.common.channel.Level;
     import io.openems.common.exceptions.OpenemsError;
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
     import io.openems.common.exceptions.OpenemsException;
    @@ -65,8 +70,16 @@
     import io.openems.common.session.Role;
     import io.openems.common.session.User;
     import io.openems.common.types.EdgeConfig;
    +import io.openems.common.types.EdgeConfig.Component.Channel.ChannelDetail;
    +import io.openems.common.types.EdgeConfig.Component.Channel.ChannelDetailOpenemsType;
    +import io.openems.common.types.EdgeConfig.Component.Channel.ChannelDetailState;
    +import io.openems.common.types.OptionsEnum;
     import io.openems.common.utils.JsonUtils;
    +import io.openems.edge.common.channel.Channel;
    +import io.openems.edge.common.channel.Doc;
    +import io.openems.edge.common.channel.EnumDoc;
     import io.openems.edge.common.channel.StateChannel;
    +import io.openems.edge.common.channel.StateChannelDoc;
     import io.openems.edge.common.component.AbstractOpenemsComponent;
     import io.openems.edge.common.component.ComponentManager;
     import io.openems.edge.common.component.OpenemsComponent;
    @@ -110,7 +123,7 @@ public ComponentManagerImpl() {
     
     	@Activate
     	void activate(ComponentContext componentContext, BundleContext bundleContext) throws OpenemsException {
    -		super.activate(componentContext, OpenemsConstants.COMPONENT_MANAGER_ID, true);
    +		super.activate(componentContext, OpenemsConstants.COMPONENT_MANAGER_ID, "Component-Manager", true);
     
     		this.bundleContext = bundleContext;
     
    @@ -212,7 +225,7 @@ private CompletableFuture handleCreateComponentConfigReq
     		// Update Configuration
     		try {
     			this.applyConfiguration(user, config, properties);
    -		} catch (IOException e) {
    +		} catch (IOException | IllegalArgumentException e) {
     			e.printStackTrace();
     			throw OpenemsError.EDGE_UNABLE_TO_CREATE_CONFIG.exception(request.getFactoryPid(), e.getMessage());
     		}
    @@ -314,7 +327,7 @@ private Configuration getExistingConfigForId(String componentId) throws OpenemsN
     		}
     
     		// Make sure we only have one config
    -		if (configs.length == 0) {
    +		if (configs == null || configs.length == 0) {
     			throw OpenemsError.EDGE_NO_COMPONENT_WITH_ID.exception(componentId);
     		} else if (configs.length > 1) {
     			throw OpenemsError.EDGE_MULTIPLE_COMPONENTS_WITH_ID.exception(componentId);
    @@ -326,20 +339,59 @@ private Configuration getExistingConfigForId(String componentId) throws OpenemsN
     	public EdgeConfig getEdgeConfig() {
     		EdgeConfig result = new EdgeConfig();
     
    -		// get configurations that have an 'id' property -> OpenEMS Components
    +		/*
    +		 * Create Components-Map with Component-ID -> Configuration
    +		 */
    +		Map componentsMap = new HashMap<>();
    +
     		try {
    -			Configuration[] configs = this.cm.listConfigurations("(id=*)");
    -			for (Configuration config : configs) {
    +			// get configurations that have an 'id' property -> OpenEMS Components
    +			Configuration[] configurations = this.cm.listConfigurations("(id=*)");
    +
    +			// Add configurations from ConfigurationAdmin
    +			for (Configuration config : configurations) {
     				Dictionary properties = config.getProperties();
     				String componentId = properties.get("id").toString();
    +				componentsMap.put(componentId, config);
    +			}
    +		} catch (IOException | InvalidSyntaxException e) {
    +			this.logWarn(this.log,
    +					"Unable to list configurations " + e.getClass().getSimpleName() + ": " + e.getMessage());
    +		}
    +		// Add all remaining components, like singletons without ConfigurationAdmin
    +		// configuration (=null)
    +		for (OpenemsComponent component : this.components) {
    +			String componentId = component.id();
    +			if (!componentsMap.containsKey(componentId)) {
    +				componentsMap.put(component.id(), null);
    +			}
    +		}
    +		// Add myself
    +		componentsMap.put(this.id(), null);
    +
    +		/*
    +		 * Create EdgeConfig from Components-Map
    +		 */
    +		for (Entry componentEntry : componentsMap.entrySet()) {
    +			String componentId = componentEntry.getKey();
    +			String alias = componentId;
    +			TreeMap propertyMap = new TreeMap<>();
    +			String factoryPid = "";
    +
    +			Configuration config = componentEntry.getValue();
    +			if (config != null) {
    +				Dictionary properties = config.getProperties();
     				// get Factory-PID
    -				Object factoryPid = config.getFactoryPid();
    -				if (factoryPid == null) {
    -					this.logWarn(this.log, "Component [" + componentId + "] has no Factory-PID");
    -					continue;
    +				if (config.getFactoryPid() != null) {
    +					factoryPid = config.getFactoryPid().toString();
    +				}
    +
    +				// get Alias
    +				if (properties.get("alias") != null) {
    +					alias = properties.get("alias").toString();
     				}
    +
     				// get configuration properties
    -				TreeMap propertyMap = new TreeMap<>();
     				Enumeration keys = properties.keys();
     				while (keys.hasMoreElements()) {
     					String key = keys.nextElement();
    @@ -347,11 +399,53 @@ public EdgeConfig getEdgeConfig() {
     						propertyMap.put(key, JsonUtils.getAsJsonElement(properties.get(key)));
     					}
     				}
    -				result.addComponent(componentId, new EdgeConfig.Component(factoryPid.toString(), propertyMap));
     			}
    -		} catch (IOException | InvalidSyntaxException e) {
    -			this.logWarn(this.log,
    -					"Unable to list configurations " + e.getClass().getSimpleName() + ": " + e.getMessage());
    +
    +			// get Alias and Channels
    +			TreeMap channelMap = new TreeMap<>();
    +			try {
    +				OpenemsComponent component = this.getComponent(componentId);
    +				alias = component.alias();
    +				for (Channel channel : component.channels()) {
    +					io.openems.edge.common.channel.ChannelId channelId = channel.channelId();
    +					Doc doc = channelId.doc();
    +					ChannelDetail detail = null;
    +					switch (doc.getChannelCategory()) {
    +					case ENUM: {
    +						Map values = new HashMap<>();
    +						EnumDoc d = (EnumDoc) doc;
    +						for (OptionsEnum option : d.getOptions()) {
    +							values.put(option.getName(), new JsonPrimitive(option.getValue()));
    +						}
    +						detail = new EdgeConfig.Component.Channel.ChannelDetailEnum(values);
    +						break;
    +					}
    +					case OPENEMS_TYPE:
    +						detail = new ChannelDetailOpenemsType();
    +						break;
    +					case STATE:
    +						StateChannelDoc d = (StateChannelDoc) doc;
    +						Level level = d.getLevel();
    +						detail = new ChannelDetailState(level);
    +						break;
    +					}
    +					channelMap.put(channelId.id(), new EdgeConfig.Component.Channel(//
    +							channelId.id(), //
    +							doc.getType(), //
    +							doc.getAccessMode(), //
    +							doc.getText(), //
    +							doc.getUnit(), //
    +							detail //
    +					));
    +				}
    +			} catch (OpenemsNamedException e) {
    +				// Component not found. Ignore and return empty Channel-Map
    +				this.logWarn(this.log, e.getMessage());
    +			}
    +
    +			// Create EdgeConfig.Component and add it to Result
    +			result.addComponent(componentId,
    +					new EdgeConfig.Component(componentId, alias, factoryPid, propertyMap, channelMap));
     		}
     
     		final Bundle[] bundles = this.bundleContext.getBundles();
    @@ -381,7 +475,8 @@ public EdgeConfig getEdgeConfig() {
     					// Get Natures implemented by this Factory-PID
     					String[] natures = this.getNatures(bundle, manifest, factoryPid);
     					// Add Factory to config
    -					result.addFactory(factoryPid, EdgeConfig.Factory.create(objectClassDefinition, natures));
    +					result.addFactory(factoryPid,
    +							EdgeConfig.Factory.create(factoryPid, objectClassDefinition, natures));
     				}
     			}
     		}
    diff --git a/io.openems.edge.core/src/io/openems/edge/core/meta/MetaImpl.java b/io.openems.edge.core/src/io/openems/edge/core/meta/MetaImpl.java
    index 519a539906f..5a094cbe952 100644
    --- a/io.openems.edge.core/src/io/openems/edge/core/meta/MetaImpl.java
    +++ b/io.openems.edge.core/src/io/openems/edge/core/meta/MetaImpl.java
    @@ -30,7 +30,7 @@ public MetaImpl() {
     
     	@Activate
     	void activate(ComponentContext context) {
    -		super.activate(context, OpenemsConstants.META_ID, true);
    +		super.activate(context, OpenemsConstants.META_ID, "Core.Meta", true);
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java b/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java
    index 81513145ef5..de4b7b2eca2 100644
    --- a/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java
    +++ b/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java
    @@ -17,7 +17,8 @@
     import org.osgi.service.event.EventHandler;
     
     import io.openems.common.OpenemsConstants;
    -import io.openems.edge.common.channel.Level;
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Level;
     import io.openems.edge.common.channel.calculate.CalculateAverage;
     import io.openems.edge.common.channel.calculate.CalculateIntegerSum;
     import io.openems.edge.common.channel.calculate.CalculateLongSum;
    @@ -46,10 +47,10 @@
     public class SumImpl extends AbstractOpenemsComponent implements Sum, OpenemsComponent, ModbusSlave, EventHandler {
     
     	@Override
    -	public ModbusSlaveTable getModbusSlaveTable() {
    +	public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
     		return new ModbusSlaveTable(//
    -				OpenemsComponent.getModbusSlaveNatureTable(), //
    -				Sum.getModbusSlaveNatureTable());
    +				OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
    +				Sum.getModbusSlaveNatureTable(accessMode));
     	}
     
     	@Reference(policy = ReferencePolicy.DYNAMIC, //
    @@ -67,7 +68,7 @@ public SumImpl() {
     
     	@Activate
     	void activate(ComponentContext context) {
    -		super.activate(context, OpenemsConstants.SUM_ID, true);
    +		super.activate(context, OpenemsConstants.SUM_ID, "Sum", true);
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java
    index 3a61e523f4f..915fd6bf0b6 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java
    @@ -4,10 +4,11 @@
     
     import org.osgi.annotation.versioning.ProviderType;
     
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Unit;
     import io.openems.common.types.OpenemsType;
     import io.openems.edge.common.channel.Channel;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.channel.value.Value;
     import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable;
     import io.openems.edge.common.modbusslave.ModbusType;
    @@ -114,8 +115,8 @@ public Doc doc() {
     		}
     	}
     
    -	public static ModbusSlaveNatureTable getModbusSlaveNatureTable() {
    -		return ModbusSlaveNatureTable.of(AsymmetricEss.class, 100) //
    +	public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) {
    +		return ModbusSlaveNatureTable.of(AsymmetricEss.class, accessMode, 100) //
     				.channel(0, ChannelId.ACTIVE_POWER_L1, ModbusType.FLOAT32) //
     				.channel(2, ChannelId.ACTIVE_POWER_L2, ModbusType.FLOAT32) //
     				.channel(4, ChannelId.ACTIVE_POWER_L3, ModbusType.FLOAT32) //
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java
    index 6b0047bb05e..26073ea7f03 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java
    @@ -2,12 +2,12 @@
     
     import org.osgi.annotation.versioning.ProviderType;
     
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Unit;
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
     import io.openems.common.types.OpenemsType;
    -import io.openems.edge.common.channel.AccessMode;
     import io.openems.edge.common.channel.Doc;
     import io.openems.edge.common.channel.IntegerDoc;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.channel.WriteChannel;
     import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable;
     import io.openems.edge.common.modbusslave.ModbusType;
    @@ -386,8 +386,8 @@ public Doc doc() {
     		}
     	}
     
    -	public static ModbusSlaveNatureTable getModbusSlaveNatureTable() {
    -		return ModbusSlaveNatureTable.of(ManagedAsymmetricEss.class, 100) //
    +	public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) {
    +		return ModbusSlaveNatureTable.of(ManagedAsymmetricEss.class, accessMode, 100) //
     				.channel(0, ChannelId.SET_ACTIVE_POWER_L1_EQUALS, ModbusType.FLOAT32) //
     				.channel(2, ChannelId.SET_ACTIVE_POWER_L2_EQUALS, ModbusType.FLOAT32) //
     				.channel(4, ChannelId.SET_ACTIVE_POWER_L3_EQUALS, ModbusType.FLOAT32) //
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java
    index be6c63dd90e..b8b01e07bba 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java
    @@ -5,15 +5,15 @@
     import org.slf4j.LoggerFactory;
     
     import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Level;
    +import io.openems.common.channel.Unit;
     import io.openems.common.exceptions.OpenemsException;
     import io.openems.common.types.OpenemsType;
    -import io.openems.edge.common.channel.AccessMode;
     import io.openems.edge.common.channel.Channel;
     import io.openems.edge.common.channel.Doc;
     import io.openems.edge.common.channel.IntegerDoc;
    -import io.openems.edge.common.channel.Level;
     import io.openems.edge.common.channel.StateChannel;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.channel.WriteChannel;
     import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable;
     import io.openems.edge.common.modbusslave.ModbusType;
    @@ -197,8 +197,8 @@ public Doc doc() {
     		}
     	}
     
    -	public static ModbusSlaveNatureTable getModbusSlaveNatureTable() {
    -		return ModbusSlaveNatureTable.of(ManagedSymmetricEss.class, 100) //
    +	public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) {
    +		return ModbusSlaveNatureTable.of(ManagedSymmetricEss.class, accessMode, 100) //
     				.channel(0, ChannelId.ALLOWED_CHARGE_POWER, ModbusType.FLOAT32) //
     				.channel(2, ChannelId.ALLOWED_DISCHARGE_POWER, ModbusType.FLOAT32) //
     				.channel(4, ChannelId.SET_ACTIVE_POWER_EQUALS, ModbusType.FLOAT32) //
    @@ -308,11 +308,11 @@ default StateChannel getApplyPowerFailed() {
     	 * the calculated power to the ESS. If you need to constrain the allowed power,
     	 * add Constraints using the {@link #getStaticConstraints()} method.
     	 * 
    -	 * @param activePower   the active power
    -	 * @param reactivePower the reactive power
    -	 * @throws OpenemsException on error; causes activation of APPLY_POWER_FAILED
    -	 *                          StateChannel
    -	 * @throws OpenemsNamedException 
    +	 * @param activePower   the active power in [W]
    +	 * @param reactivePower the reactive power in [var]
    +	 * @throws OpenemsException      on error; causes activation of
    +	 *                               APPLY_POWER_FAILED StateChannel
    +	 * @throws OpenemsNamedException
     	 */
     	public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException;
     
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/PowerConstraint.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/PowerConstraint.java
    index 1fb31471ee6..e53f5b8cc4e 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/PowerConstraint.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/PowerConstraint.java
    @@ -2,13 +2,9 @@
     
     import java.util.function.Consumer;
     
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
     import io.openems.edge.common.channel.Channel;
     import io.openems.edge.common.channel.IntegerWriteChannel;
     import io.openems.edge.ess.power.api.Phase;
    -import io.openems.edge.ess.power.api.PowerException;
     import io.openems.edge.ess.power.api.Pwr;
     import io.openems.edge.ess.power.api.Relationship;
     
    @@ -20,8 +16,6 @@
      */
     public class PowerConstraint implements Consumer> {
     
    -	private static final Logger log = LoggerFactory.getLogger(PowerConstraint.class);
    -
     	private final String channelId;
     	private final Phase phase;
     	private final Pwr pwr;
    @@ -39,17 +33,13 @@ public void accept(Channel channel) {
     		((IntegerWriteChannel) channel).onSetNextWrite(value -> {
     			if (value != null) {
     				ManagedSymmetricEss ess = (ManagedSymmetricEss) channel.getComponent();
    -
    +				
     				// adjust value so that it fits into Min/MaxActivePower
     				value = ess.getPower().fitValueIntoMinMaxPower(ess, this.phase, this.pwr, value);
     
    -				try {
    -					ess.addPowerConstraintAndValidate("Channel [" + this.channelId + "]", this.phase, this.pwr,
    -							this.relationship, value);
    -				} catch (PowerException e) {
    -					log.error(
    -							"Unable to set power constraint from Channel [" + this.channelId + "]: " + e.getMessage());
    -				}
    +				// set power channel constraint; throws an exception on error
    +				ess.addPowerConstraintAndValidate("Channel [" + this.channelId + "]", this.phase, this.pwr,
    +						this.relationship, value);
     			}
     		});
     	}
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java
    index bf8a1b52c40..a541c39fb02 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java
    @@ -2,10 +2,11 @@
     
     import org.osgi.annotation.versioning.ProviderType;
     
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Unit;
     import io.openems.common.types.OpenemsType;
     import io.openems.edge.common.channel.Channel;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.component.OpenemsComponent;
     import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable;
     import io.openems.edge.common.modbusslave.ModbusType;
    @@ -116,8 +117,8 @@ public Doc doc() {
     
     	}
     
    -	public static ModbusSlaveNatureTable getModbusSlaveNatureTable() {
    -		return ModbusSlaveNatureTable.of(SymmetricEss.class, 100) //
    +	public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) {
    +		return ModbusSlaveNatureTable.of(SymmetricEss.class, accessMode, 100) //
     				.channel(0, ChannelId.SOC, ModbusType.UINT16) //
     				.channel(1, ChannelId.GRID_MODE, ModbusType.UINT16) //
     				.build();
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java
    index c282876ef63..d88d3a66aa8 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java
    @@ -2,12 +2,12 @@
     
     import org.osgi.annotation.versioning.ProviderType;
     
    +import io.openems.common.channel.Unit;
     import io.openems.common.types.OpenemsType;
     import io.openems.common.utils.IntUtils;
     import io.openems.common.utils.IntUtils.Round;
     import io.openems.edge.common.channel.Channel;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.component.OpenemsComponent;
     
     @ProviderType
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/Power.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/Power.java
    index f4f5b1d0a6e..26846eaa1b6 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/Power.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/Power.java
    @@ -75,29 +75,26 @@ public Constraint createSimpleConstraint(String description, ManagedSymmetricEss
     	 * @return a value that fits into Min/MaxPower
     	 */
     	public default int fitValueIntoMinMaxPower(ManagedSymmetricEss ess, Phase phase, Pwr pwr, int value) {
    -		if (value > 0) {
    -			/*
    -			 * Discharge
    -			 */
    -			// fit into max possible discharge power
    -			int maxDischargePower = this.getMaxPower(ess, phase, pwr);
    -			if (value > maxDischargePower) {
    -				Power.log.info("Reducing discharge power from [" + value + "] to [" + maxDischargePower + "] for ["
    -						+ ess.id() + pwr.getSymbol() + phase.getSymbol() + "]");
    -				value = maxDischargePower;
    -			}
    +		/*
    +		 * Discharge
    +		 */
    +		// fit into max possible discharge power
    +		int maxDischargePower = this.getMaxPower(ess, phase, pwr);
    +		if (value > maxDischargePower) {
    +			Power.log.info("Reducing power from [" + value + "] to [" + maxDischargePower + "] for [" + ess.id()
    +					+ pwr.getSymbol() + phase.getSymbol() + "]");
    +			value = maxDischargePower;
    +		}
     
    -		} else {
    -			/*
    -			 * Charge
    -			 */
    -			// fit into max possible discharge power
    -			int maxChargePower = this.getMinPower(ess, phase, pwr);
    -			if (value < maxChargePower) {
    -				Power.log.info("Reducing charge power from [" + value + "] to [" + maxChargePower + "] for ["
    -						+ ess.id() + pwr.getSymbol() + phase.getSymbol() + "]");
    -				value = maxChargePower;
    -			}
    +		/*
    +		 * Charge
    +		 */
    +		// fit into max possible discharge power
    +		int maxChargePower = this.getMinPower(ess, phase, pwr);
    +		if (value < maxChargePower) {
    +			Power.log.info("Reducing power from [" + value + "] to [" + (maxChargePower * -1) + "] for [" + ess.id()
    +					+ pwr.getSymbol() + phase.getSymbol() + "]");
    +			value = maxChargePower;
     		}
     		return value;
     	}
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java
    index d282799988f..6ffe5afc198 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.power.api;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum SolverStrategy implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/test/DummyManagedSymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/test/DummyManagedSymmetricEss.java
    index e0ca41eef17..3bd279fe447 100644
    --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/test/DummyManagedSymmetricEss.java
    +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/test/DummyManagedSymmetricEss.java
    @@ -28,7 +28,7 @@ public DummyManagedSymmetricEss(String id) {
     		for (Channel channel : this.channels()) {
     			channel.nextProcessImage();
     		}
    -		super.activate(null, id, true);
    +		super.activate(null, id, "", true);
     	}
     
     	@Override
    diff --git a/io.openems.edge.ess.byd.container/.classpath b/io.openems.edge.ess.byd.container/.classpath
    new file mode 100644
    index 00000000000..3ebd512b99a
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/.classpath
    @@ -0,0 +1,12 @@
    +
    +
    +	
    +	
    +	
    +	
    +		
    +			
    +		
    +	
    +	
    +
    diff --git a/io.openems.edge.ess.byd.container/.gitignore b/io.openems.edge.ess.byd.container/.gitignore
    new file mode 100644
    index 00000000000..c2b941a96de
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/.gitignore
    @@ -0,0 +1,2 @@
    +/bin_test/
    +/generated/
    diff --git a/io.openems.edge.ess.byd.container/.project b/io.openems.edge.ess.byd.container/.project
    new file mode 100644
    index 00000000000..413d6001e3c
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/.project
    @@ -0,0 +1,23 @@
    +
    +
    +	io.openems.edge.ess.byd.container
    +	
    +	
    +	
    +	
    +		
    +			org.eclipse.jdt.core.javabuilder
    +			
    +			
    +		
    +		
    +			bndtools.core.bndbuilder
    +			
    +			
    +		
    +	
    +	
    +		org.eclipse.jdt.core.javanature
    +		bndtools.core.bndnature
    +	
    +
    diff --git a/io.openems.edge.ess.byd.container/bnd.bnd b/io.openems.edge.ess.byd.container/bnd.bnd
    new file mode 100644
    index 00000000000..54050774268
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/bnd.bnd
    @@ -0,0 +1,25 @@
    +Bundle-Name: OpenEMS Edge ESS FENECON BYD-Container
    +Bundle-License: https://opensource.org/licenses/EPL-2.0
    +Bundle-Vendor: FENECON GmbH
    +Bundle-Version: 1.0.0.${tstamp}
    +Bundle-Description: This is a bundle which implements the BYD container.
    +
    +Export-Package: \
    +	io.openems.edge.ess.api,\
    +	io.openems.edge.ess.power.api
    +
    +Private-Package: \
    +	io.openems.edge.ess.byd.container
    +
    +-includeresource: {readme.md}
    +
    +-buildpath: ${buildpath},\
    +	io.openems.edge.ess.api;version=latest,\
    +	io.openems.edge.common;version=latest,\
    +	io.openems.common;version=latest,\
    +	io.openems.edge.bridge.modbus;version=latest
    +
    +-testpath: ${testpath}
    +
    +javac.source: 1.8
    +javac.target: 1.8
    \ No newline at end of file
    diff --git "a/io.openems.edge.ess.byd.container/doc/Standard MODBUS TCP protocol\357\274\210RTU\357\274\211 register list.doc" "b/io.openems.edge.ess.byd.container/doc/Standard MODBUS TCP protocol\357\274\210RTU\357\274\211 register list.doc"
    new file mode 100644
    index 00000000000..f742503b5f6
    Binary files /dev/null and "b/io.openems.edge.ess.byd.container/doc/Standard MODBUS TCP protocol\357\274\210RTU\357\274\211 register list.doc" differ
    diff --git a/io.openems.edge.ess.byd.container/readme.md b/io.openems.edge.ess.byd.container/readme.md
    new file mode 100644
    index 00000000000..8d048c573cb
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/readme.md
    @@ -0,0 +1,8 @@
    +# io.openems.edge.ess.byd.container Provider
    +
    +${Bundle-Description}
    +
    +## Example
    +
    +## References
    +
    diff --git a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/BatteryStringWorkState.java b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/BatteryStringWorkState.java
    new file mode 100644
    index 00000000000..4b576f27131
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/BatteryStringWorkState.java
    @@ -0,0 +1,36 @@
    +package io.openems.edge.ess.byd.container;
    +
    +import io.openems.common.types.OptionsEnum;
    +
    +public enum BatteryStringWorkState implements OptionsEnum {
    +	UNDEFINED(-1, "Undefined"), //
    +	INITIAL(0, "Initial"), //
    +	FAULT(2, "Fault"), //
    +	STARTING(4, "Starting"), //
    +	RUNNING(8, "Running"), //
    +	FAULTS(16, "Faults"),;
    +
    +	private final int value;
    +	private final String name;
    +
    +	private BatteryStringWorkState(int value, String name) {
    +		this.value = value;
    +		this.name = name;
    +	}
    +
    +	@Override
    +	public int getValue() {
    +		return value;
    +	}
    +
    +	@Override
    +	public String getName() {
    +		return name;
    +	}
    +
    +	@Override
    +	public OptionsEnum getUndefined() {
    +		return UNDEFINED;
    +	}
    +
    +}
    diff --git a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/Config.java b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/Config.java
    new file mode 100644
    index 00000000000..106e60713ad
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/Config.java
    @@ -0,0 +1,42 @@
    +package io.openems.edge.ess.byd.container;
    +
    +import org.osgi.service.metatype.annotations.AttributeDefinition;
    +import org.osgi.service.metatype.annotations.ObjectClassDefinition;
    +
    +@ObjectClassDefinition(//
    +		name = "ESS FENECON BYD Container", //
    +		description = "Implements the FENECON BYD Container")
    +@interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
    +	String id() default "ess0";
    +
    +	@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 = "Read-Only mode", description = "Enables Read-Only mode")
    +	boolean readonly() default false;
    +
    +	@AttributeDefinition(name = "Modbus-ID 0 (PCS + BECU)", description = "ID of Modbus brige to PCS and BECU.")
    +	String modbus_id0();
    +
    +	@AttributeDefinition(name = "Modbus-ID 1 (ADAS)", description = "ID of Modbus brige to ADAS.")
    +	String modbus_id1();
    +
    +	@AttributeDefinition(name = "Modbus-ID 2 (RTU)", description = "ID of Modbus brige to RTU.")
    +	String modbus_id2();
    +
    +	@AttributeDefinition(name = "Modbus0 target filter", description = "This is auto-generated by 'Modbus-ID 0'.")
    +	String Modbus_target() default "";
    +
    +	@AttributeDefinition(name = "Modbus1 target filter", description = "This is auto-generated by 'Modbus-ID 1'.")
    +	String modbus1_target() default "";
    +
    +	@AttributeDefinition(name = "Modbus2 target filter", description = "This is auto-generated by 'Modbus-ID 2'.")
    +	String modbus2_target() default "";
    +
    +	String webconsole_configurationFactory_nameHint() default "ESS FENECON BYD Container [{id}]";
    +}
    diff --git a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/EssFeneconBydContainer.java b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/EssFeneconBydContainer.java
    new file mode 100644
    index 00000000000..4e689e37848
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/EssFeneconBydContainer.java
    @@ -0,0 +1,941 @@
    +package io.openems.edge.ess.byd.container;
    +
    +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.channel.Level;
    +import io.openems.common.channel.Unit;
    +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.types.OpenemsType;
    +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
    +import io.openems.edge.bridge.modbus.api.BridgeModbus;
    +import io.openems.edge.bridge.modbus.api.ElementToChannelConverter;
    +import io.openems.edge.bridge.modbus.api.ModbusProtocol;
    +import io.openems.edge.bridge.modbus.api.element.BitsWordElement;
    +import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement;
    +import io.openems.edge.bridge.modbus.api.element.SignedWordElement;
    +import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement;
    +import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement;
    +import io.openems.edge.bridge.modbus.api.element.WordOrder;
    +import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask;
    +import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
    +import io.openems.edge.common.channel.Doc;
    +import io.openems.edge.common.channel.IntegerWriteChannel;
    +import io.openems.edge.common.component.OpenemsComponent;
    +import io.openems.edge.common.taskmanager.Priority;
    +import io.openems.edge.ess.api.ManagedSymmetricEss;
    +import io.openems.edge.ess.api.SymmetricEss;
    +import io.openems.edge.ess.power.api.Constraint;
    +import io.openems.edge.ess.power.api.Phase;
    +import io.openems.edge.ess.power.api.Power;
    +import io.openems.edge.ess.power.api.Pwr;
    +import io.openems.edge.ess.power.api.Relationship;
    +
    +@Designate(ocd = Config.class, factory = true)
    +@Component(//
    +		name = "Ess.Fenecon.BydContainer", //
    +		immediate = true, //
    +		configurationPolicy = ConfigurationPolicy.REQUIRE)
    +public class EssFeneconBydContainer extends AbstractOpenemsModbusComponent
    +		implements ManagedSymmetricEss, SymmetricEss, OpenemsComponent {
    +
    +	private static final int UNIT_ID = 100;
    +	private boolean readonly = false;
    +
    +	@Reference
    +	protected ConfigurationAdmin cm;
    +
    +	@Reference
    +	private Power power;
    +
    +	public EssFeneconBydContainer() {
    +		super(//
    +				OpenemsComponent.ChannelId.values(), //
    +				SymmetricEss.ChannelId.values(), //
    +				ManagedSymmetricEss.ChannelId.values(), //
    +				ChannelId.values() //
    +		);
    +	}
    +
    +	@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
    +	protected void setModbus(BridgeModbus modbus) {
    +		super.setModbus(modbus);
    +	}
    +
    +	@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
    +	protected BridgeModbus modbus1;
    +
    +	@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
    +	protected BridgeModbus modbus2;
    +
    +	@Activate
    +	void activate(ComponentContext context, Config config) {
    +		super.activate(context, config.id(), config.alias(), config.enabled(), UNIT_ID, this.cm, "Modbus",
    +				config.modbus_id0());
    +
    +		// Configure Modbus 1
    +		if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "modbus1", config.modbus_id1())) {
    +			return;
    +		}
    +		if (this.isEnabled() && this.modbus1 != null) {
    +			this.modbus1.addProtocol(this.id(), this.defineModbus1Protocol());
    +		}
    +
    +		// Configure Modbus 2
    +		if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "modbus2", config.modbus_id2())) {
    +			return;
    +		}
    +		if (this.isEnabled() && this.modbus2 != null) {
    +			this.modbus2.addProtocol(this.id(), this.defineModbus2Protocol());
    +		}
    +
    +		// Handle Read-Only mode
    +		this.readonly = config.readonly();
    +		this.channel(ChannelId.READ_ONLY_MODE).setNextValue(config.readonly());
    +	}
    +
    +	@Deactivate
    +	protected void deactivate() {
    +		if (this.modbus1 != null) {
    +			this.modbus1.removeProtocol(this.id());
    +		}
    +		if (this.modbus2 != null) {
    +			this.modbus2.removeProtocol(this.id());
    +		}
    +		super.deactivate();
    +	}
    +
    +	@Override
    +	public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException {
    +		if (this.readonly) {
    +			return;
    +		}
    +
    +		IntegerWriteChannel setActivePowerChannel = this.channel(ChannelId.SET_ACTIVE_POWER);
    +		IntegerWriteChannel setReactivePowerChannel = this.channel(ChannelId.SET_REACTIVE_POWER);
    +		setActivePowerChannel.setNextWriteValue(activePower / 1000);
    +		setReactivePowerChannel.setNextWriteValue(reactivePower / 1000);
    +	}
    +
    +	@Override
    +	public Power getPower() {
    +		return this.power;
    +	}
    +
    +	@Override
    +	public int getPowerPrecision() {
    +		return 1000;
    +	}
    +
    +	@Override
    +	public Constraint[] getStaticConstraints() {
    +		// Handle Read-Only mode -> no charge/discharge
    +		if (this.readonly) {
    +			return new Constraint[] { //
    +					this.createPowerConstraint("Read-Only-Mode", Phase.ALL, Pwr.ACTIVE, Relationship.EQUALS, 0), //
    +					this.createPowerConstraint("Read-Only-Mode", Phase.ALL, Pwr.REACTIVE, Relationship.EQUALS, 0) //
    +			};
    +		}
    +
    +		// System is not running -> no charge/discharge
    +		SystemWorkmode systemWorkmode = this.channel(ChannelId.SYSTEM_WORKMODE).value().asEnum(); //
    +		SystemWorkstate systemWorkstate = this.channel(ChannelId.SYSTEM_WORKSTATE).value().asEnum();//
    +		if (systemWorkmode != SystemWorkmode.PQ_MODE || systemWorkstate != SystemWorkstate.RUNNING) {
    +			return new Constraint[] { //
    +					this.createPowerConstraint("WorkMode+State invalid", //
    +							Phase.ALL, Pwr.ACTIVE, Relationship.EQUALS, 0),
    +					this.createPowerConstraint("WorkMode+State invalid", //
    +							Phase.ALL, Pwr.REACTIVE, Relationship.EQUALS, 0) };
    +		}
    +		// TODO set the positive and negative power limit in Constraints
    +		// IntegerReadChannel posReactivePowerLimit =
    +		// this.channel(ChannelId.POSITIVE_REACTIVE_POWER_LIMIT);//
    +		// int positiveReactivePowerLimit = TypeUtils.getAsType(OpenemsType.INTEGER,
    +		// posReactivePowerLimit);
    +		// IntegerReadChannel negReactivePowerLimit =
    +		// this.channel(ChannelId.NEGATIVE_REACTIVE_POWER_LIMIT);//
    +		// int negativeReactivePowerLimit = TypeUtils.getAsType(OpenemsType.INTEGER,
    +		// posReactivePowerLimit);
    +		// return new Constraint[] { //
    +		// this.createPowerConstraint("Positive Reactive Power Limit", Phase.ALL,
    +		// Pwr.REACTIVE,//
    +		// Relationship.LESS_OR_EQUALS, positiveReactivePowerLimit), //
    +		// this.createPowerConstraint("Negative Reactive Power Limit", Phase.ALL,
    +		// Pwr.REACTIVE, //
    +		// Relationship.GREATER_OR_EQUALS, negativeReactivePowerLimit) //
    +		// };
    +		// TODO set reactive power limit from limitInductiveReactivePower +
    +		// limitCapacitiveReactivePower
    +		// IntegerReadChannel limitInductiveReactivePower =
    +		// this.channel(ChannelId.LIMIT_INDUCTIVE_REACTIVE_POWER);
    +		// IntegerReadChannel limitCapacitiveReactivePower =
    +		// this.channel(ChannelId.LIMIT_CAPACITIVE_REACTIVE_POWER);
    +		return Power.NO_CONSTRAINTS;
    +	}
    +
    +	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
    +		READ_ONLY_MODE(Doc.of(Level.INFO)),
    +		// RTU registers
    +		SYSTEM_WORKSTATE(Doc.of(SystemWorkstate.values())), //
    +		SYSTEM_WORKMODE(Doc.of(SystemWorkmode.values())), //
    +		LIMIT_INDUCTIVE_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOVOLT_AMPERE_REACTIVE)),
    +		LIMIT_CAPACITIVE_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOVOLT_AMPERE_REACTIVE)),
    +		CONTAINER_RUN_NUMBER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		SET_SYSTEM_WORKSTATE(Doc.of(SetSystemWorkstate.values())//
    +				.accessMode(AccessMode.WRITE_ONLY)),
    +		SET_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOWATT)//
    +				.accessMode(AccessMode.WRITE_ONLY)), //
    +		SET_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOVOLT_AMPERE_REACTIVE)//
    +				.accessMode(AccessMode.WRITE_ONLY)), //
    +		// PCS registers
    +		PCS_SYSTEM_WORKSTATE(Doc.of(SystemWorkstate.values())), //
    +		PCS_SYSTEM_WORKMODE(Doc.of(SystemWorkmode.values())), //
    +		PHASE3_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOWATT)), //
    +		PHASE3_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOVOLT_AMPERE_REACTIVE)), //
    +		PHASE3_INSPECTING_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOVOLT_AMPERE)), //
    +		PCS_DISCHARGE_LIMIT_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOWATT)), //
    +		PCS_CHARGE_LIMIT_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOWATT)), //
    +		POSITIVE_REACTIVE_POWER_LIMIT(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOVOLT_AMPERE_REACTIVE)), //
    +		NEGATIVE_REACTIVE_POWER_LIMIT(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOVOLT_AMPERE_REACTIVE)), //
    +		CURRENT_L1(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.AMPERE)), //
    +		CURRENT_L2(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.AMPERE)), //
    +		CURRENT_L3(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.AMPERE)), //
    +		VOLTAGE_L1(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		VOLTAGE_L2(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		VOLTAGE_L3(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		VOLTAGE_L12(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		VOLTAGE_L23(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		VOLTAGE_L31(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		SYSTEM_FREQUENCY(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.HERTZ)),
    +		DC_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		DC_CURRENT(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.AMPERE)), //
    +		DC_POWER(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.KILOWATT)), //
    +		IGBT_TEMPERATURE_L1(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		IGBT_TEMPERATURE_L2(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		IGBT_TEMPERATURE_L3(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		// PCS_WARNING_0
    +		STATE_0(Doc.of(Level.WARNING).text("DC pre-charging contactor checkback abnormal")),
    +		STATE_1(Doc.of(Level.WARNING).text("AC pre-charging contactor checkback abnormal")),
    +		STATE_2(Doc.of(Level.WARNING).text("AC main contactor checkback abnormal")),
    +		STATE_3(Doc.of(Level.WARNING).text("AC circuit breaker checkback abnormal")),
    +		STATE_4(Doc.of(Level.WARNING).text("Container door open")), //
    +		STATE_5(Doc.of(Level.WARNING).text("Reserved")),
    +		STATE_6(Doc.of(Level.WARNING).text("AC circuit breaker is not closed")),
    +		STATE_7(Doc.of(Level.WARNING).text("Reserved")),
    +		// PCS_WARNING_1
    +		STATE_8(Doc.of(Level.WARNING).text("General overload")), //
    +		STATE_9(Doc.of(Level.WARNING).text("Severe overload")),
    +		STATE_10(Doc.of(Level.WARNING).text("Over temperature drop power")),
    +		STATE_11(Doc.of(Level.WARNING).text("AC three-phase current imbalance alarm")),
    +		STATE_12(Doc.of(Level.WARNING).text("Failed to reset factory settings")),
    +		STATE_13(Doc.of(Level.WARNING).text("Hardware board invalidation")),
    +		STATE_14(Doc.of(Level.WARNING).text("Self-test failure alarm")),
    +		STATE_15(Doc.of(Level.WARNING).text("Receive BMS stop signal")),
    +		STATE_16(Doc.of(Level.WARNING).text("Air-conditioner")),
    +		STATE_17(Doc.of(Level.WARNING).text("IGBT Three phase temperature difference is large")),
    +		STATE_18(Doc.of(Level.WARNING).text("EEPROM Input data overrun")),
    +		STATE_19(Doc.of(Level.WARNING).text("Back up EEPROM data failure")),
    +		STATE_20(Doc.of(Level.WARNING).text("DC circuit breaker checkback abnormal")),
    +		STATE_21(Doc.of(Level.WARNING).text("DC main contactor checkback abnormal")),
    +		// PCS_WARNING_2
    +		STATE_22(Doc.of(Level.WARNING).text("Interruption of communication between PCS and Master")),
    +		STATE_23(Doc.of(Level.WARNING).text("Interruption of communication between PCS and unit controller")),
    +		STATE_24(Doc.of(Level.WARNING).text("Excessive temperature")),
    +		STATE_25(Doc.of(Level.WARNING).text("Excessive humidity")),
    +		STATE_26(Doc.of(Level.WARNING).text("Accept H31 control board signal shutdown")),
    +		STATE_27(Doc.of(Level.WARNING).text("Radiator A temperature sampling failure")),
    +		STATE_28(Doc.of(Level.WARNING).text("Radiator B temperature sampling failure")),
    +		STATE_29(Doc.of(Level.WARNING).text("Radiator C temperature sampling failure")),
    +		STATE_30(Doc.of(Level.WARNING).text("Reactor temperature sampling failure")),
    +		STATE_31(Doc.of(Level.WARNING).text("PCS cabinet environmental temperature sampling failure")),
    +		STATE_32(Doc.of(Level.WARNING).text("DC circuit breaker not engaged")),
    +		STATE_33(Doc.of(Level.WARNING).text("Controller of receive system shutdown because of abnormal command")),
    +		// PCS_WARNING_3
    +		STATE_34(Doc.of(Level.WARNING).text("Interruption of communication between PCS and RTU0 ")),
    +		STATE_35(Doc.of(Level.WARNING).text("Interruption of communication between PCS and RTU1AN")),
    +		STATE_36(Doc.of(Level.WARNING).text("Interruption of communication between PCS and MasterCAN")),
    +		STATE_37(Doc.of(Level.WARNING).text("Short-term access too many times to hot standby status in a short term")),
    +		STATE_38(Doc.of(Level.WARNING).text("entry and exit dynamic monitoring too many times in a short term")),
    +		STATE_39(Doc.of(Level.WARNING).text("AC preload contactor delay closure ")),
    +		// PCS_FAULTS_0
    +		STATE_40(Doc.of(Level.FAULT).text("DC pre-charge contactor cannot pull in")),
    +		STATE_41(Doc.of(Level.FAULT).text("AC pre-charge contactor cannot pull in")),
    +		STATE_42(Doc.of(Level.FAULT).text("AC main contactor cannot pull in")),
    +		STATE_43(Doc.of(Level.FAULT).text("AC breaker is abnormally disconnected during operation")),
    +		STATE_44(Doc.of(Level.FAULT).text("AC main contactor disconnected during operation")),
    +		STATE_45(Doc.of(Level.FAULT).text("AC main contactor cannot be disconnected")),
    +		STATE_46(Doc.of(Level.FAULT).text("Hardware PDP failure")),
    +		STATE_47(Doc.of(Level.FAULT).text("DC midpoint 1 high voltage protection")),
    +		STATE_48(Doc.of(Level.FAULT).text("DC midpoint 2 high voltage protection")),
    +		// PCS_FAULTS_1
    +		STATE_49(Doc.of(Level.FAULT).text("Radiator A over-temperature protection")),
    +		STATE_50(Doc.of(Level.FAULT).text("Radiator B over-temperature protection")),
    +		STATE_51(Doc.of(Level.FAULT).text("Radiator C over-temperature protection")),
    +		STATE_52(Doc.of(Level.FAULT).text("Electric reactor core over temperature protection")),
    +		STATE_53(Doc.of(Level.FAULT).text("DC breaker disconnected abnormally in operation")),
    +		STATE_54(Doc.of(Level.FAULT).text("DC main contactor disconnected abnormally in operation")),
    +		// PCS_FAULTS_2
    +		STATE_55(Doc.of(Level.FAULT).text("DC short-circuit protection")),
    +		STATE_56(Doc.of(Level.FAULT).text("DC overvoltage protection")),
    +		STATE_57(Doc.of(Level.FAULT).text("DC undervoltage protection")),
    +		STATE_58(Doc.of(Level.FAULT).text("DC reverse or missed connection protection")),
    +		STATE_59(Doc.of(Level.FAULT).text("DC disconnection protection")),
    +		STATE_60(Doc.of(Level.FAULT).text("DC overcurrent protection")),
    +		STATE_61(Doc.of(Level.FAULT).text("AC Phase A Peak Protection")),
    +		STATE_62(Doc.of(Level.FAULT).text("AC Phase B Peak Protection")),
    +		STATE_63(Doc.of(Level.FAULT).text("AC Phase C Peak Protection")),
    +		STATE_64(Doc.of(Level.FAULT).text("AC phase A effective value high protection")),
    +		STATE_65(Doc.of(Level.FAULT).text("AC phase B effective value high protection")),
    +		STATE_66(Doc.of(Level.FAULT).text("AC phase C effective value high protection")),
    +		STATE_67(Doc.of(Level.FAULT).text("A-phase voltage sampling Failure")),
    +		STATE_68(Doc.of(Level.FAULT).text("B-phase voltage sampling Failure")),
    +		STATE_69(Doc.of(Level.FAULT).text("C-phase voltage sampling Failure")),
    +		// PCS_FAULTS_3
    +		STATE_70(Doc.of(Level.FAULT).text("Inverted Phase A Voltage Sampling Failure")),
    +		STATE_71(Doc.of(Level.FAULT).text("Inverted Phase B Voltage Sampling Failure")),
    +		STATE_72(Doc.of(Level.FAULT).text("Inverted Phase C Voltage Sampling Failure")),
    +		STATE_73(Doc.of(Level.FAULT).text("AC current sampling failure")),
    +		STATE_74(Doc.of(Level.FAULT).text("DC current sampling failure")),
    +		STATE_75(Doc.of(Level.FAULT).text("Phase A over-temperature protection")),
    +		STATE_76(Doc.of(Level.FAULT).text("Phase B over-temperature protection")),
    +		STATE_77(Doc.of(Level.FAULT).text("Phase C over-temperature protection")),
    +		STATE_78(Doc.of(Level.FAULT).text("A phase temperature sampling failure")),
    +		STATE_79(Doc.of(Level.FAULT).text("B phase temperature sampling failure")),
    +		STATE_80(Doc.of(Level.FAULT).text("C phase temperature sampling failure")),
    +		STATE_81(Doc.of(Level.FAULT).text("AC Phase A not fully pre-charged under-protection")),
    +		STATE_82(Doc.of(Level.FAULT).text("AC Phase B not fully pre-charged under-protection")),
    +		STATE_83(Doc.of(Level.FAULT).text("AC Phase C not fully pre-charged under-protection")),
    +		STATE_84(Doc.of(Level.FAULT).text("Non-adaptable phase sequence error protection")),
    +		STATE_85(Doc.of(Level.FAULT).text("DSP protection")),
    +		// PCS_FAULTS_4
    +		STATE_86(Doc.of(Level.FAULT).text("A-phase grid voltage serious high protection")),
    +		STATE_87(Doc.of(Level.FAULT).text("A-phase grid voltage general high protection")),
    +		STATE_88(Doc.of(Level.FAULT).text("B-phase grid voltage serious high protection")),
    +		STATE_89(Doc.of(Level.FAULT).text("B-phase grid voltage general high protection")),
    +		STATE_90(Doc.of(Level.FAULT).text("C-phase grid voltage serious high protection")),
    +		STATE_91(Doc.of(Level.FAULT).text("C-phase grid voltage general high protection")),
    +		STATE_92(Doc.of(Level.FAULT).text("A-phase grid voltage serious low  protection")),
    +		STATE_93(Doc.of(Level.FAULT).text("A-phase grid voltage general low protection")),
    +		STATE_94(Doc.of(Level.FAULT).text("B-phase grid voltage serious low  protection")),
    +		STATE_95(Doc.of(Level.FAULT).text("B-phase grid voltage general low protection")),
    +		STATE_96(Doc.of(Level.FAULT).text("C-phase grid voltage serious low  protection")),
    +		STATE_97(Doc.of(Level.FAULT).text("C-phase grid voltage general low protection")),
    +		STATE_98(Doc.of(Level.FAULT).text("serious high frequency")),
    +		STATE_99(Doc.of(Level.FAULT).text("general high frequency")),
    +		STATE_100(Doc.of(Level.FAULT).text("serious low frequency")),
    +		STATE_101(Doc.of(Level.FAULT).text("general low frequency")),
    +		// PCS_FAULTS_5
    +		STATE_102(Doc.of(Level.FAULT).text("Grid A phase loss")),
    +		STATE_103(Doc.of(Level.FAULT).text("Grid B phase loss")),
    +		STATE_104(Doc.of(Level.FAULT).text("Grid C phase loss")),
    +		STATE_105(Doc.of(Level.FAULT).text("Island protection")),
    +		STATE_106(Doc.of(Level.FAULT).text("A-phase low voltage ride through")),
    +		STATE_107(Doc.of(Level.FAULT).text("B-phase low voltage ride through")),
    +		STATE_108(Doc.of(Level.FAULT).text("C-phase low voltage ride through")),
    +		STATE_109(Doc.of(Level.FAULT).text("A phase inverter voltage serious high protection")),
    +		STATE_110(Doc.of(Level.FAULT).text("A phase inverter voltage general high protection")),
    +		STATE_111(Doc.of(Level.FAULT).text("B phase inverter voltage serious high protection")),
    +		STATE_112(Doc.of(Level.FAULT).text("B phase inverter voltage general high protection")),
    +		STATE_113(Doc.of(Level.FAULT).text("C phase inverter voltage serious high protection")),
    +		STATE_114(Doc.of(Level.FAULT).text("C phase inverter voltage general high protection")),
    +		STATE_115(Doc.of(Level.FAULT).text("Inverter peak voltage high protection cause by AC disconnection")),
    +		// BECU registers
    +		BATTERY_STRING_WORK_STATE(Doc.of(BatteryStringWorkState.values())),
    +		BATTERY_STRING_TOTAL_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)), //
    +		BATTERY_STRING_CURRENT(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.AMPERE)),
    +		BATTERY_STRING_SOC(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		BATTERY_STRING_AVERAGE_TEMPERATURE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		BATTERY_NUMBER_MAX_STRING_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		BATTERY_STRING_MAX_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)),
    +		BATTERY_STRING_MAX_VOLTAGE_TEMPARATURE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		BATTERY_NUMBER_MIN_STRING_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		BATTERY_STRING_MIN_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)),
    +		BATTERY_STRING_MIN_VOLTAGE_TEMPARATURE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		BATTERY_NUMBER_MAX_STRING_TEMPERATURE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		BATTERY_STRING_MAX_TEMPERATURE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		BATTERY_STRING_MAX_TEMPARATURE_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)),
    +		BATTERY_NUMBER_MIN_STRING_TEMPERATURE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		BATTERY_STRING_MIN_TEMPERATURE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.DEGREE_CELSIUS)),
    +		BATTERY_STRING_MIN_TEMPARATURE_VOLTAGE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.VOLT)),
    +		BATTERY_STRING_CHARGE_CURRENT_LIMIT(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.AMPERE)),
    +		BATTERY_STRING_DISCHARGE_CURRENT_LIMIT(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.AMPERE)),
    +		// BATTERY_STRING_WARNING_0_0
    +		STATE_116(Doc.of(Level.WARNING).text("Charging overcurrent general alarm")), //
    +		STATE_117(Doc.of(Level.WARNING).text("discharging overcurrent general alarm")), //
    +		STATE_118(Doc.of(Level.WARNING).text("Charge current over-limit alarm")), //
    +		STATE_119(Doc.of(Level.WARNING).text("discharge current over-limit alarm")), //
    +		STATE_120(Doc.of(Level.WARNING).text("General high voltage alarm")), //
    +		STATE_121(Doc.of(Level.WARNING).text("General low voltage alarm")), //
    +		STATE_122(Doc.of(Level.WARNING).text("Abnormal voltage change alarm")), //
    +		STATE_123(Doc.of(Level.WARNING).text("General high temperature alarm")), //
    +		STATE_124(Doc.of(Level.WARNING).text("General low temperature alarm")), //
    +		STATE_125(Doc.of(Level.WARNING).text("Abnormal temperature change alarm")), //
    +		STATE_126(Doc.of(Level.WARNING).text("Severe high voltage alarm")), //
    +		STATE_127(Doc.of(Level.WARNING).text("Severe low voltage alarm")), //
    +		STATE_128(Doc.of(Level.WARNING).text("Severe low temperature alarm")), //
    +		STATE_129(Doc.of(Level.WARNING).text("Charge current severe over-limit alarm")), //
    +		STATE_130(Doc.of(Level.WARNING).text("Discharge current severe over-limit alarm")), //
    +		STATE_131(Doc.of(Level.WARNING).text("Total voltage over limit alarm")), //
    +		// BATTERY_STRING_WARNING_0_1
    +		STATE_132(Doc.of(Level.WARNING).text("Balanced sampling abnormal alarm")), //
    +		STATE_133(Doc.of(Level.WARNING).text("Balanced control abnormal alarm")), //
    +		STATE_134(Doc.of(Level.WARNING).text("Isolation switch is not closed")), //
    +		STATE_135(Doc.of(Level.WARNING).text("Pre-charge current abnormal")), //
    +		STATE_136(Doc.of(Level.WARNING).text("Disconnected contactor current is not safe")), //
    +		STATE_137(Doc.of(Level.WARNING).text("Value of the current limit reduce")), //
    +		STATE_138(Doc.of(Level.WARNING).text("Isolation Switch Checkback Abnormal")), //
    +		STATE_139(Doc.of(Level.WARNING).text("Over temperature drop power")), //
    +		STATE_140(Doc.of(Level.WARNING).text("Pulse charge approaching maximum load time")), //
    +		STATE_141(Doc.of(Level.WARNING).text("Pulse charge timeout alarm")), //
    +		STATE_142(Doc.of(Level.WARNING).text("Pulse discharge approaching maximum load time")), //
    +		STATE_143(Doc.of(Level.WARNING).text("Pulse discharge timeout alarm")), //
    +		STATE_144(Doc.of(Level.WARNING).text("Battery string undervoltage")), //
    +		STATE_145(Doc.of(Level.WARNING).text("High voltage offset")), //
    +		STATE_146(Doc.of(Level.WARNING).text("Low pressure offset")), //
    +		STATE_147(Doc.of(Level.WARNING).text("High temperature offset")), //
    +		// BATTERY_STRING_WARNING_1_0
    +		STATE_148(Doc.of(Level.FAULT).text("Start timeout")), //
    +		STATE_149(Doc.of(Level.FAULT).text("Total operating voltage sampling abnormal")), //
    +		STATE_150(Doc.of(Level.FAULT).text("BMU Sampling circuit abnormal")), //
    +		STATE_151(Doc.of(Level.FAULT).text("Stop total voltage sampling abnormal")), //
    +		STATE_152(Doc.of(Level.FAULT).text("voltage sampling line open")), //
    +		STATE_153(Doc.of(Level.FAULT).text("Temperature sample line open")), //
    +		STATE_154(Doc.of(Level.FAULT).text("Main-auxiliary internal CAN open")), //
    +		STATE_155(Doc.of(Level.FAULT).text("Interruption with system controller communication")), //
    +		// BATTERY_STRING_WARNING_1_1
    +		STATE_156(Doc.of(Level.FAULT).text("Severe high temperature failure")), //
    +		STATE_157(Doc.of(Level.FAULT).text("Smoke alarm")), //
    +		STATE_158(Doc.of(Level.FAULT).text("Fuse failure")), //
    +		STATE_159(Doc.of(Level.FAULT).text("General leakage")), //
    +		STATE_160(Doc.of(Level.FAULT).text("Severe leakage")), //
    +		STATE_161(Doc.of(Level.FAULT).text("Repair switch disconnected")), //
    +		STATE_162(Doc.of(Level.FAULT).text("Emergency stop pressed down")), //
    +		// ADAS register addresses
    +		CONTAINER_IMMERSION_STATE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)), //
    +		CONTAINER_FIRE_STATUS(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)), //
    +		CONTROL_CABINET_STATE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)), //
    +		CONTAINER_GROUNDING_FAULT(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		CONTAINER_DOOR_STATUS_0(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)), //
    +		CONTAINER_DOOR_STATUS_1(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		CONTAINER_AIRCONDITION_POWER_SUPPLY_STATE(Doc.of(OpenemsType.INTEGER)//
    +				.unit(Unit.NONE)),
    +		// ADAS_WARNING_0_0
    +		STATE_163(Doc.of(Level.WARNING).text("ups1 Power down")), //
    +		STATE_164(Doc.of(Level.WARNING).text("Immersion sensor abnormal")), //
    +		STATE_165(Doc.of(Level.WARNING).text("switch 2 (battery room door) abnormal")), //
    +		STATE_166(Doc.of(Level.WARNING).text("switch 1 (PCS room door) abnormal")), //
    +		STATE_167(Doc.of(Level.WARNING).text("Firefighting fault")), //
    +		STATE_168(Doc.of(Level.WARNING).text("Lightning arrester abnormal")), //
    +		STATE_169(Doc.of(Level.WARNING).text("Fire alarm")), //
    +		STATE_170(Doc.of(Level.WARNING).text("Fire detector works")), //
    +		STATE_171(Doc.of(Level.WARNING).text("pcs1 Ground fault alarm signal")), //
    +		STATE_172(Doc.of(Level.WARNING).text("Integrated fire extinguishing")), //
    +		STATE_173(Doc.of(Level.WARNING).text("Emergency stop signal")), //
    +		STATE_174(Doc.of(Level.WARNING).text("Air conditioning fault contactor signal")), //
    +		STATE_175(Doc.of(Level.WARNING).text("pcs1 Ground fault shutdown signal")), //
    +		// ADAS_WARNING_0_1
    +		STATE_176(Doc.of(Level.WARNING).text("PCS room ambient temperature sensor failure")), //
    +		STATE_177(Doc.of(Level.WARNING).text("Battery room ambient temperature sensor failure")), //
    +		STATE_178(Doc.of(Level.WARNING).text("container external ambient temperature sensor failure")), //
    +		STATE_179(Doc.of(Level.WARNING).text("The temperature sensor on the top of the control cabinet failed")), //
    +		STATE_180(Doc.of(Level.WARNING).text("PCS room ambient humidity sensor failure")), //
    +		STATE_181(Doc.of(Level.WARNING).text("Battery room ambient humidity sensor failure")), //
    +		STATE_182(Doc.of(Level.WARNING).text("container external humidity sensor failure")), //
    +		STATE_183(Doc.of(Level.WARNING).text("SD card failure")), //
    +		STATE_184(Doc.of(Level.WARNING).text("PCS room ambient humidity alarm")), //
    +		STATE_185(Doc.of(Level.WARNING).text("battery room ambient humidity alarm")), //
    +		STATE_186(Doc.of(Level.WARNING).text("container external humidity alarm")), //
    +		// ADAS_WARNING_0_2
    +		STATE_187(Doc.of(Level.WARNING).text("Master Firefighting fault")), //
    +		STATE_188(Doc.of(Level.WARNING).text("Harmonic protection")), //
    +		STATE_189(Doc.of(Level.WARNING).text("Battery emergency stop")), //
    +		// ADAS_WARNING_1_0
    +		STATE_190(Doc.of(Level.FAULT).text("Reserved")),
    +		// ADAS_WARNING_1_1
    +		STATE_191(Doc.of(Level.FAULT).text("Serious overheating of ambient temperature in PCS room")), //
    +		STATE_192(Doc.of(Level.FAULT).text("Serious overheating of ambient temperature in Battery room")), //
    +		STATE_193(Doc.of(Level.FAULT).text("Serious overheating of ambient temperature outside the container")), //
    +		STATE_194(Doc.of(Level.FAULT).text("Serious overheating on the top of the electric control cabinet ")), //
    +		STATE_195(Doc.of(Level.FAULT).text("Serious overheating of transformer")), //
    +		// ADAS_WARNING_1_2
    +		STATE_196(Doc.of(Level.FAULT).text("DCAC module 1 Communication disconnected")), //
    +		STATE_197(Doc.of(Level.FAULT).text("DCAC module 2 Communication disconnected")), //
    +		STATE_198(Doc.of(Level.FAULT).text("DCAC module 3 Communication disconnected")), //
    +		STATE_199(Doc.of(Level.FAULT).text("DCAC module 4 Communication disconnected")), //
    +		STATE_200(Doc.of(Level.FAULT).text("BECU1 Communication disconnected")), //
    +		STATE_201(Doc.of(Level.FAULT).text("BECU2 Communication disconnected")), //
    +		STATE_202(Doc.of(Level.FAULT).text("BECU3 Communication disconnected")), //
    +		STATE_203(Doc.of(Level.FAULT).text("BECU4 Communication disconnected"));//
    +
    +		private final Doc doc;
    +
    +		private ChannelId(Doc doc) {
    +			this.doc = doc;
    +		}
    +
    +		@Override
    +		public Doc doc() {
    +			return this.doc;
    +		}
    +	}
    +
    +	@Override
    +	protected ModbusProtocol defineModbusProtocol() {
    +		return new ModbusProtocol(this, new FC3ReadRegistersTask(0x1001, Priority.LOW,
    +				// TODO check each channels id's for scaling factor
    +				m(EssFeneconBydContainer.ChannelId.PCS_SYSTEM_WORKSTATE, new UnsignedWordElement(0x1001)),
    +				m(EssFeneconBydContainer.ChannelId.PCS_SYSTEM_WORKMODE, new UnsignedWordElement(0x1002)),
    +				m(EssFeneconBydContainer.ChannelId.PHASE3_ACTIVE_POWER, new SignedWordElement(0x1003)),
    +				m(EssFeneconBydContainer.ChannelId.PHASE3_REACTIVE_POWER, new SignedWordElement(0x1004)),
    +				m(EssFeneconBydContainer.ChannelId.PHASE3_INSPECTING_POWER, new UnsignedWordElement(0x1005)),
    +				m(EssFeneconBydContainer.ChannelId.PCS_DISCHARGE_LIMIT_ACTIVE_POWER, new UnsignedWordElement(0x1006)),
    +				m(EssFeneconBydContainer.ChannelId.PCS_CHARGE_LIMIT_ACTIVE_POWER, new SignedWordElement(0x1007)),
    +				m(EssFeneconBydContainer.ChannelId.POSITIVE_REACTIVE_POWER_LIMIT, new UnsignedWordElement(0x1008)),
    +				m(EssFeneconBydContainer.ChannelId.NEGATIVE_REACTIVE_POWER_LIMIT, new SignedWordElement(0x1009)),
    +				m(EssFeneconBydContainer.ChannelId.CURRENT_L1, new SignedWordElement(0x100A),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.CURRENT_L2, new SignedWordElement(0x100B),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.CURRENT_L3, new SignedWordElement(0x100C),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.VOLTAGE_L1, new UnsignedWordElement(0x100D),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.VOLTAGE_L2, new UnsignedWordElement(0x100E),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.VOLTAGE_L3, new UnsignedWordElement(0x100F),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.VOLTAGE_L12, new UnsignedWordElement(0x1010),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.VOLTAGE_L23, new UnsignedWordElement(0x1011),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.VOLTAGE_L31, new UnsignedWordElement(0x1012),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(EssFeneconBydContainer.ChannelId.SYSTEM_FREQUENCY, new UnsignedWordElement(0x1013)),
    +				m(EssFeneconBydContainer.ChannelId.DC_VOLTAGE, new SignedWordElement(0x1014)),
    +				m(EssFeneconBydContainer.ChannelId.DC_CURRENT, new SignedWordElement(0x1015)),
    +				m(EssFeneconBydContainer.ChannelId.DC_POWER, new SignedWordElement(0x1016)),
    +				m(EssFeneconBydContainer.ChannelId.IGBT_TEMPERATURE_L1, new SignedWordElement(0x1017)),
    +				m(EssFeneconBydContainer.ChannelId.IGBT_TEMPERATURE_L2, new SignedWordElement(0x1018)),
    +				m(EssFeneconBydContainer.ChannelId.IGBT_TEMPERATURE_L3, new SignedWordElement(0x1019)),
    +				new DummyRegisterElement(0x101A, 0x103F), //
    +				// PCS_WARNING_0
    +				m(new BitsWordElement(0x1040, this) //
    +						.bit(0, EssFeneconBydContainer.ChannelId.STATE_0) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_1) //
    +						.bit(4, EssFeneconBydContainer.ChannelId.STATE_2) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_3) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_4) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_5) //
    +						.bit(14, EssFeneconBydContainer.ChannelId.STATE_6) //
    +						.bit(15, EssFeneconBydContainer.ChannelId.STATE_7) //
    +				),
    +				// PCS_WARNING_1
    +				m(new BitsWordElement(0x1041, this) //
    +						.bit(0, EssFeneconBydContainer.ChannelId.STATE_8) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_9) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_10) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_11) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_12) //
    +						.bit(7, EssFeneconBydContainer.ChannelId.STATE_13) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_14) //
    +						.bit(9, EssFeneconBydContainer.ChannelId.STATE_15) //
    +						.bit(10, EssFeneconBydContainer.ChannelId.STATE_16) //
    +						.bit(11, EssFeneconBydContainer.ChannelId.STATE_17) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_18) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_19) //
    +						.bit(14, EssFeneconBydContainer.ChannelId.STATE_20) //
    +						.bit(15, EssFeneconBydContainer.ChannelId.STATE_21) //
    +				),
    +				// PCS_WARNING_2
    +				m(new BitsWordElement(0x1042, this) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_22) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_23) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_24) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_25) //
    +						.bit(7, EssFeneconBydContainer.ChannelId.STATE_26) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_27) //
    +						.bit(9, EssFeneconBydContainer.ChannelId.STATE_28) //
    +						.bit(10, EssFeneconBydContainer.ChannelId.STATE_29) //
    +						.bit(11, EssFeneconBydContainer.ChannelId.STATE_30) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_31) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_32) //
    +						.bit(14, EssFeneconBydContainer.ChannelId.STATE_33) //
    +				),
    +				// PCS_WARNING_3
    +				m(new BitsWordElement(0x1043, this) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_34) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_35) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_36) //
    +						.bit(4, EssFeneconBydContainer.ChannelId.STATE_37) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_38) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_39) //
    +				), //
    +				new DummyRegisterElement(0x1044, 0X104F), //
    +				// PCS_FAULTS_0
    +				m(new BitsWordElement(0x1050, this) //
    +						.bit(0, EssFeneconBydContainer.ChannelId.STATE_40) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_41) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_42) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_43) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_44) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_45) //
    +						.bit(11, EssFeneconBydContainer.ChannelId.STATE_46) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_47) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_48) //
    +				),
    +				// PCS_FAULTS_1
    +				m(new BitsWordElement(0x1051, this) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_49) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_50) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_51) //
    +						.bit(4, EssFeneconBydContainer.ChannelId.STATE_52) //
    +						.bit(7, EssFeneconBydContainer.ChannelId.STATE_53) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_54) //
    +				), m(new BitsWordElement(0x1052, this) //
    +						.bit(0, EssFeneconBydContainer.ChannelId.STATE_55) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_56) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_57) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_58) //
    +						.bit(4, EssFeneconBydContainer.ChannelId.STATE_59) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_60) //
    +						.bit(7, EssFeneconBydContainer.ChannelId.STATE_61) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_62) //
    +						.bit(9, EssFeneconBydContainer.ChannelId.STATE_63) //
    +						.bit(10, EssFeneconBydContainer.ChannelId.STATE_64) //
    +						.bit(11, EssFeneconBydContainer.ChannelId.STATE_65) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_66) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_67) //
    +						.bit(14, EssFeneconBydContainer.ChannelId.STATE_68) //
    +						.bit(15, EssFeneconBydContainer.ChannelId.STATE_69) //
    +				),
    +				// PCS_FAULTS_3
    +				m(new BitsWordElement(0x1053, this) //
    +						.bit(0, EssFeneconBydContainer.ChannelId.STATE_70) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_71) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_72) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_73) //
    +						.bit(4, EssFeneconBydContainer.ChannelId.STATE_74) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_75) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_76) //
    +						.bit(7, EssFeneconBydContainer.ChannelId.STATE_77) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_78) //
    +						.bit(9, EssFeneconBydContainer.ChannelId.STATE_79) //
    +						.bit(10, EssFeneconBydContainer.ChannelId.STATE_80) //
    +						.bit(11, EssFeneconBydContainer.ChannelId.STATE_81) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_82) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_83) //
    +						.bit(14, EssFeneconBydContainer.ChannelId.STATE_84) //
    +						.bit(15, EssFeneconBydContainer.ChannelId.STATE_85) //
    +				),
    +				// PCS_FAULTS_4
    +				m(new BitsWordElement(0x1054, this) //
    +						.bit(0, EssFeneconBydContainer.ChannelId.STATE_86) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_87) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_88) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_89) //
    +						.bit(4, EssFeneconBydContainer.ChannelId.STATE_90) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_91) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_92) //
    +						.bit(7, EssFeneconBydContainer.ChannelId.STATE_93) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_94) //
    +						.bit(9, EssFeneconBydContainer.ChannelId.STATE_95) //
    +						.bit(10, EssFeneconBydContainer.ChannelId.STATE_96) //
    +						.bit(11, EssFeneconBydContainer.ChannelId.STATE_97) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_98) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_99) //
    +						.bit(14, EssFeneconBydContainer.ChannelId.STATE_100) //
    +						.bit(15, EssFeneconBydContainer.ChannelId.STATE_101) //
    +				),
    +				// PCS_FAULTS_5
    +				m(new BitsWordElement(0x1055, this) //
    +						.bit(0, EssFeneconBydContainer.ChannelId.STATE_102) //
    +						.bit(1, EssFeneconBydContainer.ChannelId.STATE_103) //
    +						.bit(2, EssFeneconBydContainer.ChannelId.STATE_104) //
    +						.bit(3, EssFeneconBydContainer.ChannelId.STATE_105) //
    +						.bit(4, EssFeneconBydContainer.ChannelId.STATE_106) //
    +						.bit(5, EssFeneconBydContainer.ChannelId.STATE_107) //
    +						.bit(6, EssFeneconBydContainer.ChannelId.STATE_108) //
    +						.bit(7, EssFeneconBydContainer.ChannelId.STATE_109) //
    +						.bit(8, EssFeneconBydContainer.ChannelId.STATE_110) //
    +						.bit(9, EssFeneconBydContainer.ChannelId.STATE_111) //
    +						.bit(10, EssFeneconBydContainer.ChannelId.STATE_112) //
    +						.bit(11, EssFeneconBydContainer.ChannelId.STATE_113) //
    +						.bit(12, EssFeneconBydContainer.ChannelId.STATE_114) //
    +						.bit(13, EssFeneconBydContainer.ChannelId.STATE_115) //
    +				)),
    +				// BECU registers
    +				new FC3ReadRegistersTask(0x6001, Priority.LOW,
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_WORK_STATE, new UnsignedWordElement(0x6001)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_TOTAL_VOLTAGE,
    +								new UnsignedWordElement(0x6002)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_CURRENT, new SignedWordElement(0x6003)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_SOC, new UnsignedWordElement(0x6004)),
    +						new DummyRegisterElement(0x6005),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_AVERAGE_TEMPERATURE,
    +								new SignedWordElement(0x6006)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_NUMBER_MAX_STRING_VOLTAGE,
    +								new UnsignedWordElement(0x6007)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MAX_VOLTAGE, new UnsignedWordElement(0x6008)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MAX_VOLTAGE_TEMPARATURE,
    +								new SignedWordElement(0x6009)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_NUMBER_MIN_STRING_VOLTAGE,
    +								new UnsignedWordElement(0x600A)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MIN_VOLTAGE, new UnsignedWordElement(0x600B)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MIN_VOLTAGE_TEMPARATURE,
    +								new SignedWordElement(0x600C)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_NUMBER_MAX_STRING_TEMPERATURE,
    +								new UnsignedWordElement(0x600D)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MAX_TEMPERATURE,
    +								new SignedWordElement(0x600E)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MAX_TEMPARATURE_VOLTAGE,
    +								new UnsignedWordElement(0x600F)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_NUMBER_MIN_STRING_TEMPERATURE,
    +								new UnsignedWordElement(0x6010)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MIN_TEMPERATURE,
    +								new SignedWordElement(0x6011)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_MIN_TEMPARATURE_VOLTAGE,
    +								new UnsignedWordElement(0x6012)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_CHARGE_CURRENT_LIMIT,
    +								new UnsignedWordElement(0x6013)),
    +						m(EssFeneconBydContainer.ChannelId.BATTERY_STRING_DISCHARGE_CURRENT_LIMIT,
    +								new UnsignedWordElement(0x6014)),
    +						m(SymmetricEss.ChannelId.ACTIVE_CHARGE_ENERGY,
    +								new UnsignedDoublewordElement(0x6015).wordOrder(WordOrder.LSWMSW)),
    +						m(SymmetricEss.ChannelId.ACTIVE_DISCHARGE_ENERGY,
    +								new UnsignedDoublewordElement(0x6017).wordOrder(WordOrder.LSWMSW)),
    +						new DummyRegisterElement(0X6019, 0X603F),
    +						// BATTERY_STRING_WARNING_0_0
    +						m(new BitsWordElement(0x6040, this) //
    +								.bit(0, EssFeneconBydContainer.ChannelId.STATE_116) //
    +								.bit(1, EssFeneconBydContainer.ChannelId.STATE_117) //
    +								.bit(2, EssFeneconBydContainer.ChannelId.STATE_118) //
    +								.bit(3, EssFeneconBydContainer.ChannelId.STATE_119) //
    +								.bit(4, EssFeneconBydContainer.ChannelId.STATE_120) //
    +								.bit(5, EssFeneconBydContainer.ChannelId.STATE_121) //
    +								.bit(6, EssFeneconBydContainer.ChannelId.STATE_122) //
    +								.bit(7, EssFeneconBydContainer.ChannelId.STATE_123) //
    +								.bit(8, EssFeneconBydContainer.ChannelId.STATE_124) //
    +								.bit(9, EssFeneconBydContainer.ChannelId.STATE_125) //
    +								.bit(10, EssFeneconBydContainer.ChannelId.STATE_126) //
    +								.bit(11, EssFeneconBydContainer.ChannelId.STATE_127) //
    +								.bit(12, EssFeneconBydContainer.ChannelId.STATE_128) //
    +								.bit(13, EssFeneconBydContainer.ChannelId.STATE_129) //
    +								.bit(14, EssFeneconBydContainer.ChannelId.STATE_130) //
    +								.bit(15, EssFeneconBydContainer.ChannelId.STATE_131) //
    +						),
    +						// BATTERY_STRING_WARNING_0_1
    +						m(new BitsWordElement(0x6041, this) //
    +								.bit(0, EssFeneconBydContainer.ChannelId.STATE_132) //
    +								.bit(1, EssFeneconBydContainer.ChannelId.STATE_133) //
    +								.bit(2, EssFeneconBydContainer.ChannelId.STATE_134) //
    +								.bit(3, EssFeneconBydContainer.ChannelId.STATE_135) //
    +								.bit(4, EssFeneconBydContainer.ChannelId.STATE_136) //
    +								.bit(5, EssFeneconBydContainer.ChannelId.STATE_137) //
    +								.bit(6, EssFeneconBydContainer.ChannelId.STATE_138) //
    +								.bit(7, EssFeneconBydContainer.ChannelId.STATE_139) //
    +								.bit(8, EssFeneconBydContainer.ChannelId.STATE_140) //
    +								.bit(9, EssFeneconBydContainer.ChannelId.STATE_141) //
    +								.bit(10, EssFeneconBydContainer.ChannelId.STATE_142) //
    +								.bit(11, EssFeneconBydContainer.ChannelId.STATE_143) //
    +								.bit(12, EssFeneconBydContainer.ChannelId.STATE_144) //
    +								.bit(13, EssFeneconBydContainer.ChannelId.STATE_145) //
    +								.bit(14, EssFeneconBydContainer.ChannelId.STATE_146) //
    +								.bit(15, EssFeneconBydContainer.ChannelId.STATE_147) //
    +						),
    +						// BATTERY_STRING_WARNING_1_0
    +						m(new BitsWordElement(0x6042, this) //
    +								.bit(0, EssFeneconBydContainer.ChannelId.STATE_148) //
    +								.bit(1, EssFeneconBydContainer.ChannelId.STATE_149) //
    +								.bit(2, EssFeneconBydContainer.ChannelId.STATE_150) //
    +								.bit(3, EssFeneconBydContainer.ChannelId.STATE_151) //
    +								.bit(4, EssFeneconBydContainer.ChannelId.STATE_152) //
    +								.bit(5, EssFeneconBydContainer.ChannelId.STATE_153) //
    +								.bit(6, EssFeneconBydContainer.ChannelId.STATE_154) //
    +								.bit(7, EssFeneconBydContainer.ChannelId.STATE_155) //
    +						),
    +						// BATTERY_STRING_WARNING_1_1
    +						m(new BitsWordElement(0x6043, this) //
    +								.bit(2, EssFeneconBydContainer.ChannelId.STATE_156) //
    +								.bit(7, EssFeneconBydContainer.ChannelId.STATE_157) //
    +								.bit(8, EssFeneconBydContainer.ChannelId.STATE_158) //
    +								.bit(10, EssFeneconBydContainer.ChannelId.STATE_159) //
    +								.bit(11, EssFeneconBydContainer.ChannelId.STATE_160) //
    +								.bit(13, EssFeneconBydContainer.ChannelId.STATE_161) //
    +								.bit(14, EssFeneconBydContainer.ChannelId.STATE_162) //
    +						)));
    +	}
    +
    +	private ModbusProtocol defineModbus1Protocol() {
    +		return new ModbusProtocol(this,
    +				new FC3ReadRegistersTask(0x3410, Priority.LOW,
    +						m(EssFeneconBydContainer.ChannelId.CONTAINER_IMMERSION_STATE, new UnsignedWordElement(0x3410)),
    +						m(EssFeneconBydContainer.ChannelId.CONTAINER_FIRE_STATUS, new UnsignedWordElement(0x3411)),
    +						m(EssFeneconBydContainer.ChannelId.CONTROL_CABINET_STATE, new UnsignedWordElement(0x3412)),
    +						m(EssFeneconBydContainer.ChannelId.CONTAINER_GROUNDING_FAULT, new UnsignedWordElement(0x3413)),
    +						m(EssFeneconBydContainer.ChannelId.CONTAINER_DOOR_STATUS_0, new UnsignedWordElement(0x3414)),
    +						m(EssFeneconBydContainer.ChannelId.CONTAINER_DOOR_STATUS_1, new UnsignedWordElement(0x3415)),
    +						m(EssFeneconBydContainer.ChannelId.CONTAINER_AIRCONDITION_POWER_SUPPLY_STATE,
    +								new UnsignedWordElement(0x3416)),
    +						new DummyRegisterElement(0X3417, 0X343F),
    +						// ADAS_WARNING_0_0
    +						m(new BitsWordElement(0x3440, this) //
    +								.bit(0, EssFeneconBydContainer.ChannelId.STATE_163) //
    +								.bit(2, EssFeneconBydContainer.ChannelId.STATE_164) //
    +								.bit(4, EssFeneconBydContainer.ChannelId.STATE_165) //
    +								.bit(5, EssFeneconBydContainer.ChannelId.STATE_166) //
    +								.bit(6, EssFeneconBydContainer.ChannelId.STATE_167) //
    +								.bit(7, EssFeneconBydContainer.ChannelId.STATE_168) //
    +								.bit(8, EssFeneconBydContainer.ChannelId.STATE_169) //
    +								.bit(9, EssFeneconBydContainer.ChannelId.STATE_170) //
    +								.bit(10, EssFeneconBydContainer.ChannelId.STATE_171) //
    +								.bit(11, EssFeneconBydContainer.ChannelId.STATE_172) //
    +								.bit(12, EssFeneconBydContainer.ChannelId.STATE_173) //
    +								.bit(13, EssFeneconBydContainer.ChannelId.STATE_174) //
    +								.bit(14, EssFeneconBydContainer.ChannelId.STATE_175) //
    +						),
    +						// ADAS_WARNING_0_1
    +						m(new BitsWordElement(0x3441, this) //
    +								.bit(3, EssFeneconBydContainer.ChannelId.STATE_176) //
    +								.bit(4, EssFeneconBydContainer.ChannelId.STATE_177) //
    +								.bit(5, EssFeneconBydContainer.ChannelId.STATE_178) //
    +								.bit(6, EssFeneconBydContainer.ChannelId.STATE_179) //
    +								.bit(9, EssFeneconBydContainer.ChannelId.STATE_180) //
    +								.bit(10, EssFeneconBydContainer.ChannelId.STATE_181) //
    +								.bit(11, EssFeneconBydContainer.ChannelId.STATE_182) //
    +								.bit(12, EssFeneconBydContainer.ChannelId.STATE_183) //
    +								.bit(13, EssFeneconBydContainer.ChannelId.STATE_184) //
    +								.bit(14, EssFeneconBydContainer.ChannelId.STATE_185) //
    +								.bit(15, EssFeneconBydContainer.ChannelId.STATE_186) //
    +						),
    +						// ADAS_WARNING_0_2
    +						m(new BitsWordElement(0x3442, this) //
    +								.bit(0, EssFeneconBydContainer.ChannelId.STATE_187) //
    +								.bit(2, EssFeneconBydContainer.ChannelId.STATE_188) //
    +								.bit(3, EssFeneconBydContainer.ChannelId.STATE_189) //
    +						), //
    +						new DummyRegisterElement(0X3443, 0X344F),
    +						// ADAS_WARNING_1_0
    +						m(new BitsWordElement(0x3450, this) //
    +								.bit(0, EssFeneconBydContainer.ChannelId.STATE_190) //
    +						), //
    +							// ADAS_WARNING_1_1
    +						m(new BitsWordElement(0x3451, this) //
    +								.bit(3, EssFeneconBydContainer.ChannelId.STATE_190) //
    +								.bit(4, EssFeneconBydContainer.ChannelId.STATE_191) //
    +								.bit(5, EssFeneconBydContainer.ChannelId.STATE_192) //
    +								.bit(6, EssFeneconBydContainer.ChannelId.STATE_193) //
    +								.bit(9, EssFeneconBydContainer.ChannelId.STATE_194) //
    +						),
    +						// ADAS_WARNING_1_2
    +						m(new BitsWordElement(0x3452, this) //
    +								.bit(0, EssFeneconBydContainer.ChannelId.STATE_195) //
    +								.bit(1, EssFeneconBydContainer.ChannelId.STATE_196) //
    +								.bit(2, EssFeneconBydContainer.ChannelId.STATE_197) //
    +								.bit(3, EssFeneconBydContainer.ChannelId.STATE_198) //
    +								.bit(8, EssFeneconBydContainer.ChannelId.STATE_199) //
    +								.bit(9, EssFeneconBydContainer.ChannelId.STATE_200) //
    +								.bit(10, EssFeneconBydContainer.ChannelId.STATE_201) //
    +								.bit(11, EssFeneconBydContainer.ChannelId.STATE_202) //
    +						)));
    +	}
    +
    +	protected ModbusProtocol defineModbus2Protocol() {
    +		return new ModbusProtocol(this, new FC3ReadRegistersTask(0x38A0, Priority.LOW, //
    +				// RTU registers
    +				m(EssFeneconBydContainer.ChannelId.SYSTEM_WORKSTATE, new UnsignedWordElement(0x38A0)),
    +				m(EssFeneconBydContainer.ChannelId.SYSTEM_WORKMODE, new UnsignedWordElement(0x38A1)),
    +				m(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER, new SignedWordElement(0x38A2),
    +						ElementToChannelConverter.SCALE_FACTOR_3),
    +				m(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER, new SignedWordElement(0x38A3),
    +						ElementToChannelConverter.SCALE_FACTOR_3),
    +				m(EssFeneconBydContainer.ChannelId.LIMIT_INDUCTIVE_REACTIVE_POWER, new SignedWordElement(0x38A4)),
    +				m(EssFeneconBydContainer.ChannelId.LIMIT_CAPACITIVE_REACTIVE_POWER, new SignedWordElement(0x38A5)),
    +				m(SymmetricEss.ChannelId.ACTIVE_POWER, new SignedWordElement(0x38A6),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(SymmetricEss.ChannelId.REACTIVE_POWER, new SignedWordElement(0x38A7),
    +						ElementToChannelConverter.SCALE_FACTOR_2),
    +				m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(0x38A8)),
    +				m(EssFeneconBydContainer.ChannelId.CONTAINER_RUN_NUMBER, new UnsignedWordElement(0x38A9))),
    +				new FC16WriteRegistersTask(0x0500,
    +						m(EssFeneconBydContainer.ChannelId.SET_SYSTEM_WORKSTATE, new UnsignedWordElement(0x0500)),
    +						m(EssFeneconBydContainer.ChannelId.SET_ACTIVE_POWER, new SignedWordElement(0x0501),
    +								ElementToChannelConverter.SCALE_FACTOR_3),
    +						m(EssFeneconBydContainer.ChannelId.SET_REACTIVE_POWER, new SignedWordElement(0x0502),
    +								ElementToChannelConverter.SCALE_FACTOR_3)),
    +				new FC16WriteRegistersTask(0x0601, //
    +						m(EssFeneconBydContainer.ChannelId.SYSTEM_WORKMODE, new UnsignedWordElement(0x0601))));
    +	}
    +
    +	@Override
    +	public String debugLog() {
    +		return "SoC:" + this.getSoc().value().asString() //
    +				+ "|L:" + this.getActivePower().value().asString() //
    +				+ "|Allowed:"
    +				+ this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER).value().asStringWithoutUnit() + ";"
    +				+ this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER).value().asString() //
    +				+ "|" + this.getGridMode().value().asOptionString();
    +	}
    +
    +}
    diff --git a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SetSystemWorkstate.java b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SetSystemWorkstate.java
    new file mode 100644
    index 00000000000..01a42799a6a
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SetSystemWorkstate.java
    @@ -0,0 +1,33 @@
    +package io.openems.edge.ess.byd.container;
    +
    +import io.openems.common.types.OptionsEnum;
    +
    +public enum SetSystemWorkstate implements OptionsEnum {
    +	UNDEFINED(-1, "Undefined"), 
    +	STOP(4, "Stop"),
    +	RUN(64, "Run");
    +	
    +	private final int value;
    +	private final String name;
    +
    +	private SetSystemWorkstate(int value, String name) {
    +		this.value = value;
    +		this.name = name;
    +	}
    +
    +	@Override
    +	public int getValue() {
    +		return value;
    +	}
    +
    +	@Override
    +	public String getName() {
    +		return name;
    +	}
    +	
    +	@Override
    +	public OptionsEnum getUndefined() {
    +		return UNDEFINED;
    +	}
    +
    +}
    diff --git a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SystemWorkmode.java b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SystemWorkmode.java
    new file mode 100644
    index 00000000000..fec4a7a34c7
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SystemWorkmode.java
    @@ -0,0 +1,32 @@
    +package io.openems.edge.ess.byd.container;
    +
    +import io.openems.common.types.OptionsEnum;
    +
    +public enum SystemWorkmode implements OptionsEnum {
    +	UNDEFINED(-1, "Undefined"), 
    +	PQ_MODE(2, "PQ-mode");
    +	
    +	private final int value;
    +	private final String name;
    +
    +	private SystemWorkmode(int value, String name) {
    +		this.value = value;
    +		this.name = name;
    +	}
    +
    +	@Override
    +	public int getValue() {
    +		return value;
    +	}
    +
    +	@Override
    +	public String getName() {
    +		return name;
    +	}
    +	
    +	@Override
    +	public OptionsEnum getUndefined() {
    +		return UNDEFINED;
    +	}
    +
    +}
    diff --git a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SystemWorkstate.java b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SystemWorkstate.java
    new file mode 100644
    index 00000000000..82d29a03caf
    --- /dev/null
    +++ b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/SystemWorkstate.java
    @@ -0,0 +1,38 @@
    +package io.openems.edge.ess.byd.container;
    +
    +import io.openems.common.types.OptionsEnum;
    +
    +public enum SystemWorkstate implements OptionsEnum {
    +	UNDEFINED(-1, "Undefined"), 
    +	INITIAL(0, "Initial"), 
    +	FAULT(2, "Fault"), 
    +	STOP(4, "Stop"), 
    +	STANDBY(8, "Standby"),
    +	GRID_MONITORING(16, "Grid-Monitoring"), 
    +	READY(32, "Ready"),
    +	RUNNING(64, "Running"),
    +	DEBUG(128, "Debug");
    +
    +	private final int value;
    +	private final String name;
    +
    +	private SystemWorkstate(int value, String name) {
    +		this.value = value;
    +		this.name = name;
    +	}
    +
    +	@Override
    +	public int getValue() {
    +		return value;
    +	}
    +
    +	@Override
    +	public String getName() {
    +		return name;
    +	}
    +	
    +	@Override
    +	public OptionsEnum getUndefined() {
    +		return UNDEFINED;
    +	}
    +}
    diff --git a/io.openems.edge.ess.byd.container/test/.gitignore b/io.openems.edge.ess.byd.container/test/.gitignore
    new file mode 100644
    index 00000000000..e69de29bb2d
    diff --git a/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/Config.java b/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/Config.java
    index 71f0318acaf..6152e3522ed 100644
    --- a/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/Config.java
    +++ b/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/Config.java
    @@ -7,8 +7,14 @@
     		name = "ESS Cluster", //
     		description = "Combines several energy storage systems to one.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ess0";
     
    +	@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 = "ESS-IDs", description = "IDs of Ess devices.")
    diff --git a/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/EssCluster.java b/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/EssCluster.java
    index 186e33d1714..4ca1cc3c185 100644
    --- a/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/EssCluster.java
    +++ b/io.openems.edge.ess.cluster/src/io/openems/edge/ess/cluster/EssCluster.java
    @@ -19,6 +19,7 @@
     import org.osgi.service.event.EventHandler;
     import org.osgi.service.metatype.annotations.Designate;
     
    +import io.openems.common.channel.AccessMode;
     import io.openems.common.exceptions.OpenemsException;
     import io.openems.edge.common.channel.Doc;
     import io.openems.edge.common.channel.calculate.CalculateAverage;
    @@ -107,7 +108,6 @@ public Doc doc() {
     	public EssCluster() {
     		super(//
     				OpenemsComponent.ChannelId.values(), //
    -				MetaEss.ChannelId.values(), //
     				SymmetricEss.ChannelId.values(), //
     				ManagedSymmetricEss.ChannelId.values(), //
     				AsymmetricEss.ChannelId.values(), //
    @@ -118,7 +118,7 @@ public EssCluster() {
     
     	@Activate
     	void activate(ComponentContext context, Config config) throws OpenemsException {
    -		super.activate(context, config.id(), config.enabled());
    +		super.activate(context, config.id(), config.alias(), config.enabled());
     
     		// update filter for 'esss' component
     		if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "esss", config.ess_ids())) {
    @@ -252,14 +252,14 @@ public Power getPower() {
     	}
     
     	@Override
    -	public ModbusSlaveTable getModbusSlaveTable() {
    +	public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
     		return new ModbusSlaveTable( //
    -				OpenemsComponent.getModbusSlaveNatureTable(), //
    -				SymmetricEss.getModbusSlaveNatureTable(), //
    -				ManagedSymmetricEss.getModbusSlaveNatureTable(), //
    -				AsymmetricEss.getModbusSlaveNatureTable(), //
    -				ManagedAsymmetricEss.getModbusSlaveNatureTable(), //
    -				ModbusSlaveNatureTable.of(EssCluster.class, 300) //
    +				OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
    +				SymmetricEss.getModbusSlaveNatureTable(accessMode), //
    +				ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), //
    +				AsymmetricEss.getModbusSlaveNatureTable(accessMode), //
    +				ManagedAsymmetricEss.getModbusSlaveNatureTable(accessMode), //
    +				ModbusSlaveNatureTable.of(EssCluster.class, accessMode, 300) //
     						.build());
     	}
     }
    diff --git a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/ApparentPowerConstraintFactory.java b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/ApparentPowerConstraintFactory.java
    index cc5a458299a..884500ef39c 100644
    --- a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/ApparentPowerConstraintFactory.java
    +++ b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/ApparentPowerConstraintFactory.java
    @@ -36,25 +36,37 @@ public ApparentPowerConstraintFactory(Data parent) {
     
     	public List getConstraints(String essId, Phase phase, double apparentPower) {
     		List result = new ArrayList<>();
    -		double degreeDelta = 90.0 / CIRCLE_SECTIONS_PER_QUARTER;
    -		Point p1 = this.getPointOnCircle(apparentPower, 0);
     
    -		for (double degree = degreeDelta; Math.floor(degree) <= 360; degree += degreeDelta) {
    -			Point p2 = this.getPointOnCircle(apparentPower, degree);
    +		if (apparentPower > 0) {
    +			// Calculate 'Apparent-Power Circle'
    +			double degreeDelta = 90.0 / CIRCLE_SECTIONS_PER_QUARTER;
    +			Point p1 = this.getPointOnCircle(apparentPower, 0);
     
    -			Relationship relationship;
    -			if (Math.floor(degree) <= 180) {
    -				relationship = Relationship.GREATER_OR_EQUALS;
    -			} else {
    -				relationship = Relationship.LESS_OR_EQUALS;
    -			}
    +			for (double degree = degreeDelta; Math.floor(degree) <= 360; degree += degreeDelta) {
    +				Point p2 = this.getPointOnCircle(apparentPower, degree);
    +
    +				Relationship relationship;
    +				if (Math.floor(degree) <= 180) {
    +					relationship = Relationship.GREATER_OR_EQUALS;
    +				} else {
    +					relationship = Relationship.LESS_OR_EQUALS;
    +				}
     
    -			Constraint constraint = this.getConstraintThroughPoints(essId, phase, p1, p2, relationship);
    -			result.add(constraint);
    +				Constraint constraint = this.getConstraintThroughPoints(essId, phase, p1, p2, relationship);
    +				result.add(constraint);
     
    -			// set p2 -> p1 for next loop
    -			p1 = p2;
    +				// set p2 -> p1 for next loop
    +				p1 = p2;
    +			}
    +
    +		} else {
    +			// Add Active-/Reactive-Power = 0 constraints
    +			result.add(this.parent.createSimpleConstraint(essId + ": Max Apparent Power", essId, phase, Pwr.ACTIVE,
    +					Relationship.EQUALS, 0));
    +			result.add(this.parent.createSimpleConstraint(essId + ": Max Apparent Power", essId, phase, Pwr.REACTIVE,
    +					Relationship.EQUALS, 0));
     		}
    +
     		return result;
     	}
     
    diff --git a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Data.java b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Data.java
    index 60c3a1de329..25a2d17f955 100644
    --- a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Data.java
    +++ b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Data.java
    @@ -7,7 +7,6 @@
     import java.util.Iterator;
     import java.util.List;
     import java.util.Objects;
    -import java.util.Optional;
     import java.util.Set;
     import java.util.concurrent.CopyOnWriteArrayList;
     import java.util.stream.Collectors;
    @@ -197,34 +196,30 @@ public List createGenericEssConstraints() {
     				continue;
     			}
     
    -			Optional allowedCharge = ess.getAllowedCharge().value().asOptional();
    -			if (allowedCharge.isPresent()) {
    -				result.add(this.createSimpleConstraint(essId + ": Allowed Charge", essId, Phase.ALL, Pwr.ACTIVE,
    -						Relationship.GREATER_OR_EQUALS, allowedCharge.get()));
    -			}
    -			Optional allowedDischarge = ess.getAllowedDischarge().value().asOptional();
    -			if (allowedDischarge.isPresent()) {
    -				result.add(this.createSimpleConstraint(essId + ": Allowed Discharge", essId, Phase.ALL, Pwr.ACTIVE,
    -						Relationship.LESS_OR_EQUALS, allowedDischarge.get()));
    -			}
    -			Optional maxApparentPower = ess.getMaxApparentPower().value().asOptional();
    -			if (maxApparentPower.isPresent()) {
    -				if (ess instanceof ManagedAsymmetricEss && !this.symmetricMode
    -						&& !(ess instanceof ManagedSinglePhaseEss)) {
    -					double maxApparentPowerPerPhase = maxApparentPower.get() / 3d;
    -					for (Phase phase : Phase.values()) {
    -						if (phase == Phase.ALL) {
    -							continue; // do not add Max Apparent Power Constraint for ALL phases
    -						}
    -						result.addAll(//
    -								this.apparentPowerConstraintFactory.getConstraints(essId, phase,
    -										maxApparentPowerPerPhase));
    +			// Allowed Charge Power
    +			result.add(this.createSimpleConstraint(essId + ": Allowed Charge", //
    +					essId, Phase.ALL, Pwr.ACTIVE, Relationship.GREATER_OR_EQUALS, //
    +					ess.getAllowedCharge().value().orElse(0)));
    +
    +			// Allowed Charge Power
    +			result.add(this.createSimpleConstraint(essId + ": Allowed Discharge", //
    +					essId, Phase.ALL, Pwr.ACTIVE, Relationship.LESS_OR_EQUALS, //
    +					ess.getAllowedDischarge().value().orElse(0)));
    +
    +			// Max Apparent Power
    +			int maxApparentPower = ess.getMaxApparentPower().value().orElse(0);
    +			if (ess instanceof ManagedAsymmetricEss && !this.symmetricMode && !(ess instanceof ManagedSinglePhaseEss)) {
    +				double maxApparentPowerPerPhase = maxApparentPower / 3d;
    +				for (Phase phase : Phase.values()) {
    +					if (phase == Phase.ALL) {
    +						continue; // do not add Max Apparent Power Constraint for ALL phases
     					}
    -				} else {
     					result.addAll(//
    -							this.apparentPowerConstraintFactory.getConstraints(essId, Phase.ALL,
    -									maxApparentPower.get()));
    +							this.apparentPowerConstraintFactory.getConstraints(essId, phase, maxApparentPowerPerPhase));
     				}
    +			} else {
    +				result.addAll(//
    +						this.apparentPowerConstraintFactory.getConstraints(essId, Phase.ALL, maxApparentPower));
     			}
     		}
     		return result;
    diff --git a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/PowerComponent.java b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/PowerComponent.java
    index 576161dae33..71aba1c5252 100644
    --- a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/PowerComponent.java
    +++ b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/PowerComponent.java
    @@ -24,13 +24,13 @@
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     
    +import io.openems.common.channel.Level;
    +import io.openems.common.channel.Unit;
     import io.openems.common.types.OpenemsType;
     import io.openems.edge.common.channel.Doc;
     import io.openems.edge.common.channel.EnumReadChannel;
     import io.openems.edge.common.channel.IntegerReadChannel;
    -import io.openems.edge.common.channel.Level;
     import io.openems.edge.common.channel.StateChannel;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.component.AbstractOpenemsComponent;
     import io.openems.edge.common.component.OpenemsComponent;
     import io.openems.edge.common.event.EdgeEventConstants;
    @@ -135,7 +135,7 @@ public PowerComponent() {
     
     	@Activate
     	void activate(ComponentContext context, Map properties, Config config) {
    -		super.activate(context, "_power", true);
    +		super.activate(context, "_power", "Ess.Power", true);
     		this.data.setSymmetricMode(config.symmetricMode());
     		this.debugMode = config.debugMode();
     		this.solver.setDebugMode(config.debugMode());
    diff --git a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Solver.java b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Solver.java
    index 99d9a60cbe9..e63cd2e2e07 100644
    --- a/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Solver.java
    +++ b/io.openems.edge.ess.core/src/io/openems/edge/ess/core/power/Solver.java
    @@ -152,7 +152,7 @@ public void solve() {
     
     		// No Inverters -> nothing to do
     		if (this.data.getInverters().isEmpty()) {
    -			this.onSolvedCallback.accept(false, 0, SolverStrategy.NONE);
    +			this.onSolvedCallback.accept(true, 0, SolverStrategy.NONE);
     			return;
     		}
     		List allInverters = data.getInverters();
    diff --git a/io.openems.edge.ess.fenecon.commercial40/.settings/org.eclipse.core.resources.prefs b/io.openems.edge.ess.fenecon.commercial40/.settings/org.eclipse.core.resources.prefs
    index d84dd7bdc14..8b5094850f5 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/.settings/org.eclipse.core.resources.prefs
    +++ b/io.openems.edge.ess.fenecon.commercial40/.settings/org.eclipse.core.resources.prefs
    @@ -1,5 +1,5 @@
     eclipse.preferences.version=1
    -encoding//src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40.java=UTF-8
    +encoding//src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java=UTF-8
     encoding//src/io/openems/edge/ess/fenecon/commercial40/charger/EssDcChargerFeneconCommercial40.java=UTF-8
     encoding/bnd.bnd=UTF-8
     encoding/readme.md=UTF-8
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryMaintenanceState.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryMaintenanceState.java
    index 40e18968cc4..67cb2820e88 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryMaintenanceState.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryMaintenanceState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum BatteryMaintenanceState implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryStringSwitchState.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryStringSwitchState.java
    index 932e20e0e3e..127f61c4210 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryStringSwitchState.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BatteryStringSwitchState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum BatteryStringSwitchState implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkMode.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkMode.java
    index 1f20ab45882..31cd377c36d 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkMode.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkMode.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum BmsDcdcWorkMode implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkState.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkState.java
    index ae4befe8dcc..d88a2e33d80 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkState.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/BmsDcdcWorkState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum BmsDcdcWorkState implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/Config.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/Config.java
    index 3a352252326..056f8450026 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/Config.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/Config.java
    @@ -7,8 +7,14 @@
     		name = "ESS FENECON Commercial 40", //
     		description = "Implements the FENECON Commercial 40 energy storage system.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ess0";
     
    +	@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 = "Read-Only mode", description = "Enables Read-Only mode")
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/ControlMode.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/ControlMode.java
    index 3aaf2224e94..c4f06c1b09f 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/ControlMode.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/ControlMode.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum ControlMode implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40.java
    index 67bb96600db..de79eda983a 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40.java
    @@ -1,1611 +1,17 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import java.time.LocalDateTime;
    -import java.util.Optional;
    -
    -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.event.Event;
    -import org.osgi.service.event.EventConstants;
     import org.osgi.service.event.EventHandler;
    -import org.osgi.service.metatype.annotations.Designate;
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
     
    -import io.openems.common.exceptions.OpenemsException;
    -import io.openems.common.types.OpenemsType;
    -import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
    -import io.openems.edge.bridge.modbus.api.BridgeModbus;
    -import io.openems.edge.bridge.modbus.api.ElementToChannelConverter;
    -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.SignedWordElement;
    -import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement;
    -import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement;
    -import io.openems.edge.bridge.modbus.api.element.WordOrder;
    -import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask;
    -import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
    -import io.openems.edge.common.channel.AccessMode;
    -import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.EnumWriteChannel;
    -import io.openems.edge.common.channel.IntegerDoc;
    -import io.openems.edge.common.channel.IntegerReadChannel;
    -import io.openems.edge.common.channel.IntegerWriteChannel;
    -import io.openems.edge.common.channel.Level;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.component.OpenemsComponent;
    -import io.openems.edge.common.event.EdgeEventConstants;
     import io.openems.edge.common.modbusslave.ModbusSlave;
    -import io.openems.edge.common.modbusslave.ModbusSlaveTable;
    -import io.openems.edge.common.sum.GridMode;
    -import io.openems.edge.common.taskmanager.Priority;
    -import io.openems.edge.common.type.TypeUtils;
     import io.openems.edge.ess.api.ManagedSymmetricEss;
     import io.openems.edge.ess.api.SymmetricEss;
    -import io.openems.edge.ess.power.api.Constraint;
    -import io.openems.edge.ess.power.api.Phase;
    -import io.openems.edge.ess.power.api.Power;
    -import io.openems.edge.ess.power.api.Pwr;
    -import io.openems.edge.ess.power.api.Relationship;
    -
    -@Designate(ocd = Config.class, factory = true)
    -@Component( //
    -		name = "Ess.Fenecon.Commercial40", //
    -		immediate = true, //
    -		configurationPolicy = ConfigurationPolicy.REQUIRE, //
    -		property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_CONTROLLERS //
    -)
    -public class EssFeneconCommercial40 extends AbstractOpenemsModbusComponent
    -		implements ManagedSymmetricEss, SymmetricEss, OpenemsComponent, EventHandler, ModbusSlave {
    -
    -	private final Logger log = LoggerFactory.getLogger(EssFeneconCommercial40.class);
    -
    -	protected final static int MAX_APPARENT_POWER = 40000;
    -
    -	private final static int UNIT_ID = 100;
    -	private final static int MIN_REACTIVE_POWER = -10000;
    -	private final static int MAX_REACTIVE_POWER = 10000;
    -
    -	private String modbusBridgeId;
    -	private boolean readOnlyMode = false;
    -
    -	@Reference
    -	private Power power;
    -
    -	@Reference
    -	protected ConfigurationAdmin cm;
    -
    -	public EssFeneconCommercial40() {
    -		super(//
    -				OpenemsComponent.ChannelId.values(), //
    -				SymmetricEss.ChannelId.values(), //
    -				ManagedSymmetricEss.ChannelId.values(), //
    -				ChannelId.values() //
    -		);
    -	}
    -
    -	@Override
    -	public void applyPower(int activePower, int reactivePower) throws OpenemsException {
    -		if (this.readOnlyMode) {
    -			return;
    -		}
    -
    -		IntegerWriteChannel setActivePowerChannel = this.channel(ChannelId.SET_ACTIVE_POWER);
    -		setActivePowerChannel.setNextWriteValue(activePower);
    -		IntegerWriteChannel setReactivePowerChannel = this.channel(ChannelId.SET_REACTIVE_POWER);
    -		setReactivePowerChannel.setNextWriteValue(reactivePower);
    -	}
    -
    -	@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
    -	protected void setModbus(BridgeModbus modbus) {
    -		super.setModbus(modbus);
    -	}
    -
    -	@Activate
    -	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled(), UNIT_ID, this.cm, "Modbus", config.modbus_id());
    -		this.modbusBridgeId = config.modbus_id();
    -		this.readOnlyMode = config.readOnlyMode();
    -	}
    -
    -	@Deactivate
    -	protected void deactivate() {
    -		super.deactivate();
    -	}
    -
    -	public String getModbusBridgeId() {
    -		return modbusBridgeId;
    -	}
    -
    -	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
    -		// EnumReadChannels
    -		SYSTEM_STATE(Doc.of(SystemState.values())), //
    -		CONTROL_MODE(Doc.of(ControlMode.values())), //
    -		BATTERY_MAINTENANCE_STATE(Doc.of(BatteryMaintenanceState.values())), //
    -		INVERTER_STATE(Doc.of(InverterState.values())), //
    -		SYSTEM_MANUFACTURER(Doc.of(SystemManufacturer.values())), //
    -		SYSTEM_TYPE(Doc.of(SystemType.values())), //
    -		BATTERY_STRING_SWITCH_STATE(Doc.of(BatteryStringSwitchState.values())), //
    -		BMS_DCDC_WORK_STATE(Doc.of(BmsDcdcWorkState.values())), //
    -		BMS_DCDC_WORK_MODE(Doc.of(BmsDcdcWorkMode.values())), //
    -
    -		// EnumWriteChannels
    -		SET_WORK_STATE(Doc.of(SetWorkState.values()) //
    -				.accessMode(AccessMode.WRITE_ONLY)), //
    -
    -		// IntegerWriteChannel
    -		SET_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.WATT) //
    -				.accessMode(AccessMode.WRITE_ONLY)), //
    -		SET_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.VOLT_AMPERE_REACTIVE) //
    -				.accessMode(AccessMode.WRITE_ONLY)), //
    -		SET_PV_POWER_LIMIT(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.WATT) //
    -				.accessMode(AccessMode.WRITE_ONLY)), //
    -
    -		// IntegerReadChannels
    -		ORIGINAL_ALLOWED_CHARGE_POWER(new IntegerDoc() //
    -				.onInit(channel -> { //
    -					// on each Update to the channel -> set the ALLOWED_CHARGE_POWER value with a
    -					// delta of max 500
    -					channel.onChange(originalValueChannel -> {
    -						IntegerReadChannel currentValueChannel = channel.getComponent()
    -								.channel(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER);
    -						Optional originalValue = originalValueChannel.asOptional();
    -						Optional currentValue = currentValueChannel.value().asOptional();
    -						int value;
    -						if (!originalValue.isPresent() && !currentValue.isPresent()) {
    -							value = 0;
    -						} else if (originalValue.isPresent() && !currentValue.isPresent()) {
    -							value = originalValue.get();
    -						} else if (!originalValue.isPresent() && currentValue.isPresent()) {
    -							value = currentValue.get();
    -						} else {
    -							value = Math.max(originalValue.get(), currentValue.get() - 500);
    -						}
    -						currentValueChannel.setNextValue(value);
    -					});
    -				})), //
    -		ORIGINAL_ALLOWED_DISCHARGE_POWER(new IntegerDoc() //
    -				.onInit(channel -> { //
    -					// on each Update to the channel -> set the ALLOWED_DISCHARGE_POWER value with a
    -					// delta of max 500
    -					channel.onChange(originalValueChannel -> {
    -						IntegerReadChannel currentValueChannel = channel.getComponent()
    -								.channel(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER);
    -						Optional originalValue = originalValueChannel.asOptional();
    -						Optional currentValue = currentValueChannel.value().asOptional();
    -						int value;
    -						if (!originalValue.isPresent() && !currentValue.isPresent()) {
    -							value = 0;
    -						} else if (originalValue.isPresent() && !currentValue.isPresent()) {
    -							value = originalValue.get();
    -						} else if (!originalValue.isPresent() && currentValue.isPresent()) {
    -							value = currentValue.get();
    -						} else {
    -							value = Math.min(originalValue.get(), currentValue.get() + 500);
    -						}
    -						currentValueChannel.setNextValue(value);
    -					});
    -				})), //
    -		PROTOCOL_VERSION(Doc.of(OpenemsType.INTEGER)), //
    -		BATTERY_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		BATTERY_CURRENT(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIAMPERE)), //
    -		BATTERY_POWER(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.WATT)), //
    -		AC_CHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.WATT_HOURS)), //
    -		AC_DISCHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.WATT_HOURS)), //
    -		GRID_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.WATT)), //
    -		APPARENT_POWER(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.VOLT_AMPERE)), //
    -		CURRENT_L1(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIAMPERE)), //
    -		CURRENT_L2(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIAMPERE)), //
    -		CURRENT_L3(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIAMPERE)), //
    -		VOLTAGE_L1(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		VOLTAGE_L2(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		VOLTAGE_L3(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		FREQUENCY(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIHERTZ)), //
    -		INVERTER_VOLTAGE_L1(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		INVERTER_VOLTAGE_L2(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		INVERTER_VOLTAGE_L3(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		INVERTER_CURRENT_L1(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIAMPERE)), //
    -		INVERTER_CURRENT_L2(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIAMPERE)), //
    -		INVERTER_CURRENT_L3(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIAMPERE)), //
    -		IPM_TEMPERATURE_L1(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.DEGREE_CELSIUS)), //
    -		IPM_TEMPERATURE_L2(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.DEGREE_CELSIUS)), //
    -		IPM_TEMPERATURE_L3(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.DEGREE_CELSIUS)), //
    -		TRANSFORMER_TEMPERATURE_L2(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.DEGREE_CELSIUS)), //
    -		CELL_1_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_2_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_3_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_4_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_5_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_6_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_7_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_8_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_9_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_10_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_11_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_12_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_13_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_14_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_15_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_16_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_17_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_18_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_19_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_20_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_21_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_22_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_23_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_24_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_25_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_26_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_27_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_28_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_29_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_30_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_31_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_32_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_33_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_34_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_35_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_36_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_37_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_38_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_39_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_40_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_41_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_42_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_43_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_44_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_45_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_46_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_47_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_48_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_49_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_50_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_51_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_52_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_53_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_54_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_55_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_56_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_57_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_58_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_59_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_60_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_61_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_62_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_63_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_64_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_65_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_66_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_67_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_68_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_69_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_70_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_71_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_72_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_73_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_74_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_75_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_76_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_77_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_78_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_79_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_80_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_81_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_82_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_83_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_84_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_85_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_86_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_87_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_88_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_89_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_90_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_91_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_92_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_93_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_94_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_95_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_96_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_97_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_98_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_99_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_100_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_101_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_102_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_103_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_104_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_105_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_106_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_107_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_108_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_109_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_110_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_111_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_112_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_113_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_114_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_115_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_116_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_117_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_118_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_119_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_120_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_121_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_122_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_123_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_124_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_125_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_126_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_127_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_128_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_129_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_130_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_131_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_132_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_133_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_134_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_135_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_136_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_137_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_138_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_139_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_140_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_141_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_142_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_143_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_144_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_145_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_146_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_147_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_148_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_149_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_150_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_151_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_152_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_153_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_154_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_155_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_156_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_157_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_158_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_159_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_160_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_161_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_162_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_163_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_164_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_165_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_166_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_167_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_168_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_169_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_170_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_171_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_172_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_173_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_174_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_175_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_176_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_177_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_178_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_179_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_180_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_181_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_182_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_183_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_184_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_185_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_186_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_187_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_188_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_189_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_190_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_191_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_192_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_193_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_194_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_195_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_196_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_197_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_198_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_199_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_200_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_201_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_202_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_203_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_204_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_205_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_206_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_207_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_208_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_209_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_210_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_211_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_212_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_213_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_214_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_215_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_216_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_217_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_218_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_219_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_220_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_221_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_222_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_223_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -		CELL_224_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    -				.unit(Unit.MILLIVOLT)), //
    -
    -		// StateChannels
    -		STATE_0(Doc.of(Level.WARNING) //
    -				.text("Emergency Stop")), //
    -		STATE_1(Doc.of(Level.WARNING) //
    -				.text("Key Manual Stop")), //
    -		STATE_2(Doc.of(Level.WARNING) //
    -				.text("Transformer Phase B Temperature Sensor Invalidation")), //
    -		STATE_3(Doc.of(Level.WARNING) //
    -				.text("SD Memory Card Invalidation")), //
    -		STATE_4(Doc.of(Level.WARNING) //
    -				.text("Inverter Communication Abnormity")), //
    -		STATE_5(Doc.of(Level.WARNING) //
    -				.text("Battery Stack Communication Abnormity")), //
    -		STATE_6(Doc.of(Level.WARNING) //
    -				.text("Multifunctional Ammeter Communication Abnormity")), //
    -		STATE_7(Doc.of(Level.WARNING) //
    -				.text("Remote Communication Abnormity")), //
    -		STATE_8(Doc.of(Level.WARNING) //
    -				.text("PVDC1 Communication Abnormity")), //
    -		STATE_9(Doc.of(Level.WARNING) //
    -				.text("PVDC2 Communication Abnormity")), //
    -		STATE_10(Doc.of(Level.WARNING) //
    -				.text("Transformer Severe Overtemperature")), //
    -		STATE_11(Doc.of(Level.FAULT) //
    -				.text("DC Precharge Contactor Close Unsuccessfully")), //
    -		STATE_12(Doc.of(Level.FAULT) //
    -				.text("AC Precharge Contactor Close Unsuccessfully")), //
    -		STATE_13(Doc.of(Level.FAULT) //
    -				.text("AC Main Contactor Close Unsuccessfully")), //
    -		STATE_14(Doc.of(Level.FAULT) //
    -				.text("DC Electrical Breaker1 Close Unsuccessfully")), //
    -		STATE_15(Doc.of(Level.FAULT) //
    -				.text("DC Main Contactor Close Unsuccessfully")), //
    -		STATE_16(Doc.of(Level.FAULT) //
    -				.text("AC Breaker Trip")), //
    -		STATE_17(Doc.of(Level.FAULT) //
    -				.text("AC Main Contactor Open When Running")), //
    -		STATE_18(Doc.of(Level.FAULT) //
    -				.text("DC Main Contactor Open When Running")), //
    -		STATE_19(Doc.of(Level.FAULT) //
    -				.text("AC Main Contactor Open Unsuccessfully")), //
    -		STATE_20(Doc.of(Level.FAULT) //
    -				.text("DC Electrical Breaker1 Open Unsuccessfully")), //
    -		STATE_21(Doc.of(Level.FAULT) //
    -				.text("DC Main Contactor Open Unsuccessfully")), //
    -		STATE_22(Doc.of(Level.FAULT) //
    -				.text("Hardware PDP Fault")), //
    -		STATE_23(Doc.of(Level.FAULT) //
    -				.text("Master Stop Suddenly")), //
    -		STATE_24(Doc.of(Level.FAULT) //
    -				.text("DCShortCircuitProtection")), //
    -		STATE_25(Doc.of(Level.FAULT) //
    -				.text("DCOvervoltageProtection")), //
    -		STATE_26(Doc.of(Level.FAULT) //
    -				.text("DCUndervoltageProtection")), //
    -		STATE_27(Doc.of(Level.FAULT) //
    -				.text("DCInverseNoConnectionProtection")), //
    -		STATE_28(Doc.of(Level.FAULT) //
    -				.text("DCDisconnectionProtection")), //
    -		STATE_29(Doc.of(Level.FAULT) //
    -				.text("CommutingVoltageAbnormityProtection")), //
    -		STATE_30(Doc.of(Level.FAULT) //
    -				.text("DCOvercurrentProtection")), //
    -		STATE_31(Doc.of(Level.FAULT) //
    -				.text("Phase1PeakCurrentOverLimitProtection")), //
    -		STATE_32(Doc.of(Level.FAULT) //
    -				.text("Phase2PeakCurrentOverLimitProtection")), //
    -		STATE_33(Doc.of(Level.FAULT) //
    -				.text("Phase3PeakCurrentOverLimitProtection")), //
    -		STATE_34(Doc.of(Level.FAULT) //
    -				.text("Phase1GridVoltageSamplingInvalidation")), //
    -		STATE_35(Doc.of(Level.FAULT) //
    -				.text("Phase2VirtualCurrentOverLimitProtection")), //
    -		STATE_36(Doc.of(Level.FAULT) //
    -				.text("Phase3VirtualCurrentOverLimitProtection")), //
    -		STATE_37(Doc.of(Level.FAULT) //
    -				.text("Phase1GridVoltageSamplingInvalidation2")), //
    -		STATE_38(Doc.of(Level.FAULT) //
    -				.text("Phase2ridVoltageSamplingInvalidation")), //
    -		STATE_39(Doc.of(Level.FAULT) //
    -				.text("Phase3GridVoltageSamplingInvalidation")), //
    -		STATE_40(Doc.of(Level.FAULT) //
    -				.text("Phase1InvertVoltageSamplingInvalidation")), //
    -		STATE_41(Doc.of(Level.FAULT) //
    -				.text("Phase2InvertVoltageSamplingInvalidation")), //
    -		STATE_42(Doc.of(Level.FAULT) //
    -				.text("Phase3InvertVoltageSamplingInvalidation")), //
    -		STATE_43(Doc.of(Level.FAULT) //
    -				.text("ACCurrentSamplingInvalidation")), //
    -		STATE_44(Doc.of(Level.FAULT) //
    -				.text("DCCurrentSamplingInvalidation")), //
    -		STATE_45(Doc.of(Level.FAULT) //
    -				.text("Phase1OvertemperatureProtection")), //
    -		STATE_46(Doc.of(Level.FAULT) //
    -				.text("Phase2OvertemperatureProtection")), //
    -		STATE_47(Doc.of(Level.FAULT) //
    -				.text("Phase3OvertemperatureProtection")), //
    -		STATE_48(Doc.of(Level.FAULT) //
    -				.text("Phase1TemperatureSamplingInvalidation")), //
    -		STATE_49(Doc.of(Level.FAULT) //
    -				.text("Phase2TemperatureSamplingInvalidation")), //
    -		STATE_50(Doc.of(Level.FAULT) //
    -				.text("Phase3TemperatureSamplingInvalidation")), //
    -		STATE_51(Doc.of(Level.FAULT) //
    -				.text("Phase1PrechargeUnmetProtection")), //
    -		STATE_52(Doc.of(Level.FAULT) //
    -				.text("Phase2PrechargeUnmetProtection")), //
    -		STATE_53(Doc.of(Level.FAULT) //
    -				.text("Phase3PrechargeUnmetProtection")), //
    -		STATE_54(Doc.of(Level.FAULT) //
    -				.text("UnadaptablePhaseSequenceErrorProtection")), //
    -		STATE_55(Doc.of(Level.FAULT) //
    -				.text("DSPProtection")), //
    -		STATE_56(Doc.of(Level.FAULT) //
    -				.text("Phase1GridVoltageSevereOvervoltageProtection")), //
    -		STATE_57(Doc.of(Level.FAULT) //
    -				.text("Phase1GridVoltageGeneralOvervoltageProtection")), //
    -		STATE_58(Doc.of(Level.FAULT) //
    -				.text("Phase2GridVoltageSevereOvervoltageProtection")), //
    -		STATE_59(Doc.of(Level.FAULT) //
    -				.text("Phase2GridVoltageGeneralOvervoltageProtection")), //
    -		STATE_60(Doc.of(Level.FAULT) //
    -				.text("Phase3GridVoltageSevereOvervoltageProtection")), //
    -		STATE_61(Doc.of(Level.FAULT) //
    -				.text("Phase3GridVoltageGeneralOvervoltageProtection")), //
    -		STATE_62(Doc.of(Level.FAULT) //
    -				.text("Phase1GridVoltageSevereUndervoltageProtection")), //
    -		STATE_63(Doc.of(Level.FAULT) //
    -				.text("Phase1GridVoltageGeneralUndervoltageProtection")), //
    -		STATE_64(Doc.of(Level.FAULT) //
    -				.text("Phase2GridVoltageSevereUndervoltageProtection")), //
    -		STATE_65(Doc.of(Level.FAULT) //
    -				.text("Phase2GridVoltageGeneralUndervoltageProtection")), //
    -		STATE_66(Doc.of(Level.FAULT) //
    -				.text("Phase3GridVoltageSevereUndervoltageProtection")), //
    -		STATE_67(Doc.of(Level.FAULT) //
    -				.text("Phase3GridVoltageGeneralUndervoltageProtection")), //
    -		STATE_68(Doc.of(Level.FAULT) //
    -				.text("SevereOverfrequncyProtection")), //
    -		STATE_69(Doc.of(Level.FAULT) //
    -				.text("GeneralOverfrequncyProtection")), //
    -		STATE_70(Doc.of(Level.FAULT) //
    -				.text("SevereUnderfrequncyProtection")), //
    -		STATE_71(Doc.of(Level.FAULT) //
    -				.text("GeneralsUnderfrequncyProtection")), //
    -		STATE_72(Doc.of(Level.FAULT) //
    -				.text("Phase1Gridloss")), //
    -		STATE_73(Doc.of(Level.FAULT) //
    -				.text("Phase2Gridloss")), //
    -		STATE_74(Doc.of(Level.FAULT) //
    -				.text("Phase3Gridloss")), //
    -		STATE_75(Doc.of(Level.FAULT) //
    -				.text("IslandingProtection")), //
    -		STATE_76(Doc.of(Level.FAULT) //
    -				.text("Phase1UnderVoltageRideThrough")), //
    -		STATE_77(Doc.of(Level.FAULT) //
    -				.text("Phase2UnderVoltageRideThrough")), //
    -		STATE_78(Doc.of(Level.FAULT) //
    -				.text("Phase3UnderVoltageRideThrough")), //
    -		STATE_79(Doc.of(Level.FAULT) //
    -				.text("Phase1InverterVoltageSevereOvervoltageProtection")), //
    -		STATE_80(Doc.of(Level.FAULT) //
    -				.text("Phase1InverterVoltageGeneralOvervoltageProtection")), //
    -		STATE_81(Doc.of(Level.FAULT) //
    -				.text("Phase2InverterVoltageSevereOvervoltageProtection")), //
    -		STATE_82(Doc.of(Level.FAULT) //
    -				.text("Phase2InverterVoltageGeneralOvervoltageProtection")), //
    -		STATE_83(Doc.of(Level.FAULT) //
    -				.text("Phase3InverterVoltageSevereOvervoltageProtection")), //
    -		STATE_84(Doc.of(Level.FAULT) //
    -				.text("Phase3InverterVoltageGeneralOvervoltageProtection")), //
    -		STATE_85(Doc.of(Level.FAULT) //
    -				.text("InverterPeakVoltageHighProtectionCauseByACDisconnect")), //
    -		STATE_86(Doc.of(Level.WARNING) //
    -				.text("DCPrechargeContactorInspectionAbnormity")), //
    -		STATE_87(Doc.of(Level.WARNING) //
    -				.text("DCBreaker1InspectionAbnormity")), //
    -		STATE_88(Doc.of(Level.WARNING) //
    -				.text("DCBreaker2InspectionAbnormity")), //
    -		STATE_89(Doc.of(Level.WARNING) //
    -				.text("ACPrechargeContactorInspectionAbnormity")), //
    -		STATE_90(Doc.of(Level.WARNING) //
    -				.text("ACMainontactorInspectionAbnormity")), //
    -		STATE_91(Doc.of(Level.WARNING) //
    -				.text("ACBreakerInspectionAbnormity")), //
    -		STATE_92(Doc.of(Level.WARNING) //
    -				.text("DCBreaker1CloseUnsuccessfully")), //
    -		STATE_93(Doc.of(Level.WARNING) //
    -				.text("DCBreaker2CloseUnsuccessfully")), //
    -		STATE_94(Doc.of(Level.WARNING) //
    -				.text("ControlSignalCloseAbnormallyInspectedBySystem")), //
    -		STATE_95(Doc.of(Level.WARNING) //
    -				.text("ControlSignalOpenAbnormallyInspectedBySystem")), //
    -		STATE_96(Doc.of(Level.WARNING) //
    -				.text("NeutralWireContactorCloseUnsuccessfully")), //
    -		STATE_97(Doc.of(Level.WARNING) //
    -				.text("NeutralWireContactorOpenUnsuccessfully")), //
    -		STATE_98(Doc.of(Level.WARNING) //
    -				.text("WorkDoorOpen")), //
    -		STATE_99(Doc.of(Level.WARNING) //
    -				.text("Emergency1Stop")), //
    -		STATE_100(Doc.of(Level.WARNING) //
    -				.text("ACBreakerCloseUnsuccessfully")), //
    -		STATE_101(Doc.of(Level.WARNING) //
    -				.text("ControlSwitchStop")), //
    -		STATE_102(Doc.of(Level.WARNING) //
    -				.text("GeneralOverload")), //
    -		STATE_103(Doc.of(Level.WARNING) //
    -				.text("SevereOverload")), //
    -		STATE_104(Doc.of(Level.WARNING) //
    -				.text("BatteryCurrentOverLimit")), //
    -		STATE_105(Doc.of(Level.WARNING) //
    -				.text("PowerDecreaseCausedByOvertemperature")), //
    -		STATE_106(Doc.of(Level.WARNING) //
    -				.text("InverterGeneralOvertemperature")), //
    -		STATE_107(Doc.of(Level.WARNING) //
    -				.text("ACThreePhaseCurrentUnbalance")), //
    -		STATE_108(Doc.of(Level.WARNING) //
    -				.text("RestoreFactorySettingUnsuccessfully")), //
    -		STATE_109(Doc.of(Level.WARNING) //
    -				.text("PoleBoardInvalidation")), //
    -		STATE_110(Doc.of(Level.WARNING) //
    -				.text("SelfInspectionFailed")), //
    -		STATE_111(Doc.of(Level.WARNING) //
    -				.text("ReceiveBMSFaultAndStop")), //
    -		STATE_112(Doc.of(Level.WARNING) //
    -				.text("RefrigerationEquipmentinvalidation")), //
    -		STATE_113(Doc.of(Level.WARNING) //
    -				.text("LargeTemperatureDifferenceAmongIGBTThreePhases")), //
    -		STATE_114(Doc.of(Level.WARNING) //
    -				.text("EEPROMParametersOverRange")), //
    -		STATE_115(Doc.of(Level.WARNING) //
    -				.text("EEPROMParametersBackupFailed")), //
    -		STATE_116(Doc.of(Level.WARNING) //
    -				.text("DCBreakerCloseunsuccessfully")), //
    -		STATE_117(Doc.of(Level.WARNING) //
    -				.text("CommunicationBetweenInverterAndBSMUDisconnected")), //
    -		STATE_118(Doc.of(Level.WARNING) //
    -				.text("CommunicationBetweenInverterAndMasterDisconnected")), //
    -		STATE_119(Doc.of(Level.WARNING) //
    -				.text("CommunicationBetweenInverterAndUCDisconnected")), //
    -		STATE_120(Doc.of(Level.WARNING) //
    -				.text("BMSStartOvertimeControlledByPCS")), //
    -		STATE_121(Doc.of(Level.WARNING) //
    -				.text("BMSStopOvertimeControlledByPCS")), //
    -		STATE_122(Doc.of(Level.WARNING) //
    -				.text("SyncSignalInvalidation")), //
    -		STATE_123(Doc.of(Level.WARNING) //
    -				.text("SyncSignalContinuousCaputureFault")), //
    -		STATE_124(Doc.of(Level.WARNING) //
    -				.text("SyncSignalSeveralTimesCaputureFault")), //
    -		STATE_125(Doc.of(Level.WARNING) //
    -				.text("CurrentSamplingChannelAbnormityOnHighVoltageSide")), //
    -		STATE_126(Doc.of(Level.WARNING) //
    -				.text("CurrentSamplingChannelAbnormityOnLowVoltageSide")), //
    -		STATE_127(Doc.of(Level.WARNING) //
    -				.text("EEPROMParametersOverRange")), //
    -		STATE_128(Doc.of(Level.WARNING) //
    -				.text("UpdateEEPROMFailed")), //
    -		STATE_129(Doc.of(Level.WARNING) //
    -				.text("ReadEEPROMFailed")), //
    -		STATE_130(Doc.of(Level.WARNING) //
    -				.text("CurrentSamplingChannelAbnormityBeforeInductance")), //
    -		STATE_131(Doc.of(Level.WARNING) //
    -				.text("ReactorPowerDecreaseCausedByOvertemperature")), //
    -		STATE_132(Doc.of(Level.WARNING) //
    -				.text("IGBTPowerDecreaseCausedByOvertemperature")), //
    -		STATE_133(Doc.of(Level.WARNING) //
    -				.text("TemperatureChanel3PowerDecreaseCausedByOvertemperature")), //
    -		STATE_134(Doc.of(Level.WARNING) //
    -				.text("TemperatureChanel4PowerDecreaseCausedByOvertemperature")), //
    -		STATE_135(Doc.of(Level.WARNING) //
    -				.text("TemperatureChanel5PowerDecreaseCausedByOvertemperature")), //
    -		STATE_136(Doc.of(Level.WARNING) //
    -				.text("TemperatureChanel6PowerDecreaseCausedByOvertemperature")), //
    -		STATE_137(Doc.of(Level.WARNING) //
    -				.text("TemperatureChanel7PowerDecreaseCausedByOvertemperature")), //
    -		STATE_138(Doc.of(Level.WARNING) //
    -				.text("TemperatureChanel8PowerDecreaseCausedByOvertemperature")), //
    -		STATE_139(Doc.of(Level.WARNING) //
    -				.text("Fan1StopFailed")), //
    -		STATE_140(Doc.of(Level.WARNING) //
    -				.text("Fan2StopFailed")), //
    -		STATE_141(Doc.of(Level.WARNING) //
    -				.text("Fan3StopFailed")), //
    -		STATE_142(Doc.of(Level.WARNING) //
    -				.text("Fan4StopFailed")), //
    -		STATE_143(Doc.of(Level.WARNING) //
    -				.text("Fan1StartupFailed")), //
    -		STATE_144(Doc.of(Level.WARNING) //
    -				.text("Fan2StartupFailed")), //
    -		STATE_145(Doc.of(Level.WARNING) //
    -				.text("Fan3StartupFailed")), //
    -		STATE_146(Doc.of(Level.WARNING) //
    -				.text("Fan4StartupFailed")), //
    -		STATE_147(Doc.of(Level.WARNING) //
    -				.text("HighVoltageSideOvervoltage")), //
    -		STATE_148(Doc.of(Level.WARNING) //
    -				.text("HighVoltageSideUndervoltage")), //
    -		STATE_149(Doc.of(Level.WARNING) //
    -				.text("HighVoltageSideVoltageChangeUnconventionally")); //
    -
    -		private final Doc doc;
    -
    -		private ChannelId(Doc doc) {
    -			this.doc = doc;
    -		}
    -
    -		@Override
    -		public Doc doc() {
    -			return this.doc;
    -		}
    -	}
    -
    -	@Override
    -	protected ModbusProtocol defineModbusProtocol() {
    -		return new ModbusProtocol(this, //
    -				new FC3ReadRegistersTask(0x0101, Priority.LOW, //
    -						m(EssFeneconCommercial40.ChannelId.SYSTEM_STATE, new UnsignedWordElement(0x0101)),
    -						m(EssFeneconCommercial40.ChannelId.CONTROL_MODE, new UnsignedWordElement(0x0102)),
    -						new DummyRegisterElement(0x0103), // WorkMode: RemoteDispatch
    -						m(EssFeneconCommercial40.ChannelId.BATTERY_MAINTENANCE_STATE, new UnsignedWordElement(0x0104)),
    -						m(EssFeneconCommercial40.ChannelId.INVERTER_STATE, new UnsignedWordElement(0x0105)),
    -						m(SymmetricEss.ChannelId.GRID_MODE, new UnsignedWordElement(0x0106), //
    -								new ElementToChannelConverter((value) -> {
    -									Integer intValue = TypeUtils.getAsType(OpenemsType.INTEGER, value);
    -									if (intValue != null) {
    -										switch (intValue) {
    -										case 1:
    -											return GridMode.OFF_GRID;
    -										case 2:
    -											return GridMode.ON_GRID;
    -										}
    -									}
    -									return GridMode.UNDEFINED;
    -								})),
    -						new DummyRegisterElement(0x0107), //
    -						m(EssFeneconCommercial40.ChannelId.PROTOCOL_VERSION, new UnsignedWordElement(0x0108)),
    -						m(EssFeneconCommercial40.ChannelId.SYSTEM_MANUFACTURER, new UnsignedWordElement(0x0109)),
    -						m(EssFeneconCommercial40.ChannelId.SYSTEM_TYPE, new UnsignedWordElement(0x010A)),
    -						new DummyRegisterElement(0x010B, 0x010F), //
    -						bm(new UnsignedWordElement(0x0110)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_0, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_1, 6) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0111)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_2, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_3, 12) //
    -								.build(), //
    -						new DummyRegisterElement(0x0112, 0x0124), //
    -						bm(new UnsignedWordElement(0x0125)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_4, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_5, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_6, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_7, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_8, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_9, 9) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0126)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_10, 3) //
    -								.build(), //
    -						new DummyRegisterElement(0x0127, 0x014F), //
    -						m(EssFeneconCommercial40.ChannelId.BATTERY_STRING_SWITCH_STATE,
    -								new UnsignedWordElement(0x0150))), //
    -				new FC3ReadRegistersTask(0x0180, Priority.LOW, //
    -						bm(new UnsignedWordElement(0x0180)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_11, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_12, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_13, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_14, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_15, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_16, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_17, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_18, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_19, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_20, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_21, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_22, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_23, 12) //
    -								.build(), //
    -						new DummyRegisterElement(0x0181), //
    -						bm(new UnsignedWordElement(0x0182)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_24, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_25, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_26, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_27, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_28, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_29, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_30, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_31, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_32, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_33, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_34, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_35, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_36, 12) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_37, 13) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_38, 14) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_39, 15) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0183)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_40, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_41, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_42, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_43, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_44, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_45, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_46, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_47, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_48, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_49, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_50, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_51, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_52, 12) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_53, 13) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_54, 14) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_55, 15) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0184)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_56, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_57, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_58, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_59, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_60, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_61, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_62, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_63, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_64, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_65, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_66, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_67, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_68, 12) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_69, 13) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_70, 14) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_71, 15) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0185)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_72, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_73, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_74, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_75, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_76, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_77, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_78, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_79, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_80, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_81, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_82, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_83, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_84, 12) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_85, 13) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0186)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_86, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_87, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_88, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_89, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_90, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_91, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_92, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_93, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_94, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_95, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_96, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_97, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_98, 12) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_99, 13) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_100, 14) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_101, 15) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0187)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_102, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_103, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_104, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_105, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_106, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_107, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_108, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_109, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_110, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_111, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_112, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_113, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_114, 12) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_115, 13) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_116, 14) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0x0188)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_117, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_118, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_119, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_120, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_121, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_122, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_123, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_124, 14) //
    -								.build() //
    -				), new FC3ReadRegistersTask(0x0200, Priority.HIGH, //
    -						m(EssFeneconCommercial40.ChannelId.BATTERY_VOLTAGE, new SignedWordElement(0x0200),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.BATTERY_CURRENT, new SignedWordElement(0x0201),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.BATTERY_POWER, new SignedWordElement(0x0202),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						new DummyRegisterElement(0x0203, 0x0207),
    -						m(SymmetricEss.ChannelId.ACTIVE_CHARGE_ENERGY,
    -								new UnsignedDoublewordElement(0x0208).wordOrder(WordOrder.LSWMSW),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(SymmetricEss.ChannelId.ACTIVE_DISCHARGE_ENERGY,
    -								new UnsignedDoublewordElement(0x020A).wordOrder(WordOrder.LSWMSW),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						new DummyRegisterElement(0x020C, 0x020F), //
    -						m(EssFeneconCommercial40.ChannelId.GRID_ACTIVE_POWER, new SignedWordElement(0x0210),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(SymmetricEss.ChannelId.REACTIVE_POWER, new SignedWordElement(0x0211),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.APPARENT_POWER, new UnsignedWordElement(0x0212),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.CURRENT_L1, new SignedWordElement(0x0213),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.CURRENT_L2, new SignedWordElement(0x0214),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.CURRENT_L3, new SignedWordElement(0x0215),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						new DummyRegisterElement(0x0216, 0x218), //
    -						m(EssFeneconCommercial40.ChannelId.VOLTAGE_L1, new UnsignedWordElement(0x0219),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.VOLTAGE_L2, new UnsignedWordElement(0x021A),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.VOLTAGE_L3, new UnsignedWordElement(0x021B),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.FREQUENCY, new UnsignedWordElement(0x021C))), //
    -				new FC3ReadRegistersTask(0x0222, Priority.HIGH, //
    -						m(EssFeneconCommercial40.ChannelId.INVERTER_VOLTAGE_L1, new UnsignedWordElement(0x0222),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.INVERTER_VOLTAGE_L2, new UnsignedWordElement(0x0223),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.INVERTER_VOLTAGE_L3, new UnsignedWordElement(0x0224),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.INVERTER_CURRENT_L1, new SignedWordElement(0x0225),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.INVERTER_CURRENT_L2, new SignedWordElement(0x0226),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.INVERTER_CURRENT_L3, new SignedWordElement(0x0227),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(SymmetricEss.ChannelId.ACTIVE_POWER, new SignedWordElement(0x0228),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						new DummyRegisterElement(0x0229, 0x022F), //
    -						m(ChannelId.ORIGINAL_ALLOWED_CHARGE_POWER, new SignedWordElement(0x0230),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(ChannelId.ORIGINAL_ALLOWED_DISCHARGE_POWER, new UnsignedWordElement(0x0231),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(SymmetricEss.ChannelId.MAX_APPARENT_POWER, new UnsignedWordElement(0x0232),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						new DummyRegisterElement(0x0233, 0x23F),
    -						m(EssFeneconCommercial40.ChannelId.IPM_TEMPERATURE_L1, new SignedWordElement(0x0240)), //
    -						m(EssFeneconCommercial40.ChannelId.IPM_TEMPERATURE_L2, new SignedWordElement(0x0241)), //
    -						m(EssFeneconCommercial40.ChannelId.IPM_TEMPERATURE_L3, new SignedWordElement(0x0242)), //
    -						new DummyRegisterElement(0x0243, 0x0248), //
    -						m(EssFeneconCommercial40.ChannelId.TRANSFORMER_TEMPERATURE_L2, new SignedWordElement(0x0249))), //
    -				new FC16WriteRegistersTask(0x0500, //
    -						m(EssFeneconCommercial40.ChannelId.SET_WORK_STATE, new UnsignedWordElement(0x0500))), //
    -				new FC16WriteRegistersTask(0x0501, //
    -						m(EssFeneconCommercial40.ChannelId.SET_ACTIVE_POWER, new SignedWordElement(0x0501),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.SET_REACTIVE_POWER, new SignedWordElement(0x0502),
    -								ElementToChannelConverter.SCALE_FACTOR_2), //
    -						m(EssFeneconCommercial40.ChannelId.SET_PV_POWER_LIMIT, new UnsignedWordElement(0x0503),
    -								ElementToChannelConverter.SCALE_FACTOR_2)), //
    -				new FC3ReadRegistersTask(0xA000, Priority.LOW, //
    -						m(EssFeneconCommercial40.ChannelId.BMS_DCDC_WORK_STATE, new UnsignedWordElement(0xA000)), //
    -						m(EssFeneconCommercial40.ChannelId.BMS_DCDC_WORK_MODE, new UnsignedWordElement(0xA001))), //
    -				new FC3ReadRegistersTask(0xA100, Priority.LOW, //
    -						bm(new UnsignedWordElement(0xA100)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_125, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_126, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_127, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_128, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_129, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_130, 9) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0xA101)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_131, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_132, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_133, 2) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_134, 3) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_135, 4) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_136, 5) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_137, 6) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_138, 7) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_139, 8) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_140, 9) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_141, 10) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_142, 11) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_143, 12) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_144, 13) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_145, 14) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_146, 15) //
    -								.build(), //
    -						bm(new UnsignedWordElement(0xA102)) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_147, 0) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_148, 1) //
    -								.m(EssFeneconCommercial40.ChannelId.STATE_149, 2) //
    -								.build()), //
    -				new FC3ReadRegistersTask(0x1500, Priority.LOW,
    -						m(EssFeneconCommercial40.ChannelId.CELL_1_VOLTAGE, new UnsignedWordElement(0x1500)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_2_VOLTAGE, new UnsignedWordElement(0x1501)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_3_VOLTAGE, new UnsignedWordElement(0x1502)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_4_VOLTAGE, new UnsignedWordElement(0x1503)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_5_VOLTAGE, new UnsignedWordElement(0x1504)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_6_VOLTAGE, new UnsignedWordElement(0x1505)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_7_VOLTAGE, new UnsignedWordElement(0x1506)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_8_VOLTAGE, new UnsignedWordElement(0x1507)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_9_VOLTAGE, new UnsignedWordElement(0x1508)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_10_VOLTAGE, new UnsignedWordElement(0x1509)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_11_VOLTAGE, new UnsignedWordElement(0x150A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_12_VOLTAGE, new UnsignedWordElement(0x150B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_13_VOLTAGE, new UnsignedWordElement(0x150C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_14_VOLTAGE, new UnsignedWordElement(0x150D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_15_VOLTAGE, new UnsignedWordElement(0x150E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_16_VOLTAGE, new UnsignedWordElement(0x150F)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_17_VOLTAGE, new UnsignedWordElement(0x1510)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_18_VOLTAGE, new UnsignedWordElement(0x1511)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_19_VOLTAGE, new UnsignedWordElement(0x1512)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_20_VOLTAGE, new UnsignedWordElement(0x1513)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_21_VOLTAGE, new UnsignedWordElement(0x1514)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_22_VOLTAGE, new UnsignedWordElement(0x1515)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_23_VOLTAGE, new UnsignedWordElement(0x1516)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_24_VOLTAGE, new UnsignedWordElement(0x1517)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_25_VOLTAGE, new UnsignedWordElement(0x1518)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_26_VOLTAGE, new UnsignedWordElement(0x1519)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_27_VOLTAGE, new UnsignedWordElement(0x151A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_28_VOLTAGE, new UnsignedWordElement(0x151B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_29_VOLTAGE, new UnsignedWordElement(0x151C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_30_VOLTAGE, new UnsignedWordElement(0x151D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_31_VOLTAGE, new UnsignedWordElement(0x151E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_32_VOLTAGE, new UnsignedWordElement(0x151F)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_33_VOLTAGE, new UnsignedWordElement(0x1520)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_34_VOLTAGE, new UnsignedWordElement(0x1521)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_35_VOLTAGE, new UnsignedWordElement(0x1522)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_36_VOLTAGE, new UnsignedWordElement(0x1523)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_37_VOLTAGE, new UnsignedWordElement(0x1524)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_38_VOLTAGE, new UnsignedWordElement(0x1525)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_39_VOLTAGE, new UnsignedWordElement(0x1526)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_40_VOLTAGE, new UnsignedWordElement(0x1527)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_41_VOLTAGE, new UnsignedWordElement(0x1528)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_42_VOLTAGE, new UnsignedWordElement(0x1529)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_43_VOLTAGE, new UnsignedWordElement(0x152A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_44_VOLTAGE, new UnsignedWordElement(0x152B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_45_VOLTAGE, new UnsignedWordElement(0x152C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_46_VOLTAGE, new UnsignedWordElement(0x152D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_47_VOLTAGE, new UnsignedWordElement(0x152E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_48_VOLTAGE, new UnsignedWordElement(0x152F)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_49_VOLTAGE, new UnsignedWordElement(0x1530)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_50_VOLTAGE, new UnsignedWordElement(0x1531)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_51_VOLTAGE, new UnsignedWordElement(0x1532)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_52_VOLTAGE, new UnsignedWordElement(0x1533)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_53_VOLTAGE, new UnsignedWordElement(0x1534)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_54_VOLTAGE, new UnsignedWordElement(0x1535)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_55_VOLTAGE, new UnsignedWordElement(0x1536)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_56_VOLTAGE, new UnsignedWordElement(0x1537)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_57_VOLTAGE, new UnsignedWordElement(0x1538)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_58_VOLTAGE, new UnsignedWordElement(0x1539)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_59_VOLTAGE, new UnsignedWordElement(0x153A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_60_VOLTAGE, new UnsignedWordElement(0x153B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_61_VOLTAGE, new UnsignedWordElement(0x153C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_62_VOLTAGE, new UnsignedWordElement(0x153D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_63_VOLTAGE, new UnsignedWordElement(0x153E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_64_VOLTAGE, new UnsignedWordElement(0x153F)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_65_VOLTAGE, new UnsignedWordElement(0x1540)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_66_VOLTAGE, new UnsignedWordElement(0x1541)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_67_VOLTAGE, new UnsignedWordElement(0x1542)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_68_VOLTAGE, new UnsignedWordElement(0x1543)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_69_VOLTAGE, new UnsignedWordElement(0x1544)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_70_VOLTAGE, new UnsignedWordElement(0x1545)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_71_VOLTAGE, new UnsignedWordElement(0x1546)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_72_VOLTAGE, new UnsignedWordElement(0x1547)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_73_VOLTAGE, new UnsignedWordElement(0x1548)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_74_VOLTAGE, new UnsignedWordElement(0x1549)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_75_VOLTAGE, new UnsignedWordElement(0x154A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_76_VOLTAGE, new UnsignedWordElement(0x154B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_77_VOLTAGE, new UnsignedWordElement(0x154C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_78_VOLTAGE, new UnsignedWordElement(0x154D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_79_VOLTAGE, new UnsignedWordElement(0x154E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_80_VOLTAGE, new UnsignedWordElement(0x154F))),
    -				new FC3ReadRegistersTask(0x1550, Priority.LOW,
    -						m(EssFeneconCommercial40.ChannelId.CELL_81_VOLTAGE, new UnsignedWordElement(0x1550)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_82_VOLTAGE, new UnsignedWordElement(0x1551)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_83_VOLTAGE, new UnsignedWordElement(0x1552)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_84_VOLTAGE, new UnsignedWordElement(0x1553)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_85_VOLTAGE, new UnsignedWordElement(0x1554)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_86_VOLTAGE, new UnsignedWordElement(0x1555)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_87_VOLTAGE, new UnsignedWordElement(0x1556)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_88_VOLTAGE, new UnsignedWordElement(0x1557)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_89_VOLTAGE, new UnsignedWordElement(0x1558)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_90_VOLTAGE, new UnsignedWordElement(0x1559)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_91_VOLTAGE, new UnsignedWordElement(0x155A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_92_VOLTAGE, new UnsignedWordElement(0x155B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_93_VOLTAGE, new UnsignedWordElement(0x155C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_94_VOLTAGE, new UnsignedWordElement(0x155D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_95_VOLTAGE, new UnsignedWordElement(0x155E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_96_VOLTAGE, new UnsignedWordElement(0x155F)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_97_VOLTAGE, new UnsignedWordElement(0x1560)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_98_VOLTAGE, new UnsignedWordElement(0x1561)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_99_VOLTAGE, new UnsignedWordElement(0x1562)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_100_VOLTAGE, new UnsignedWordElement(0x1563)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_101_VOLTAGE, new UnsignedWordElement(0x1564)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_102_VOLTAGE, new UnsignedWordElement(0x1565)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_103_VOLTAGE, new UnsignedWordElement(0x1566)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_104_VOLTAGE, new UnsignedWordElement(0x1567)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_105_VOLTAGE, new UnsignedWordElement(0x1568)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_106_VOLTAGE, new UnsignedWordElement(0x1569)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_107_VOLTAGE, new UnsignedWordElement(0x156A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_108_VOLTAGE, new UnsignedWordElement(0x156B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_109_VOLTAGE, new UnsignedWordElement(0x156C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_110_VOLTAGE, new UnsignedWordElement(0x156D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_111_VOLTAGE, new UnsignedWordElement(0x156E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_112_VOLTAGE, new UnsignedWordElement(0x156F)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_113_VOLTAGE, new UnsignedWordElement(0x1570)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_114_VOLTAGE, new UnsignedWordElement(0x1571)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_115_VOLTAGE, new UnsignedWordElement(0x1572)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_116_VOLTAGE, new UnsignedWordElement(0x1573)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_117_VOLTAGE, new UnsignedWordElement(0x1574)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_118_VOLTAGE, new UnsignedWordElement(0x1575)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_119_VOLTAGE, new UnsignedWordElement(0x1576)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_120_VOLTAGE, new UnsignedWordElement(0x1577)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_121_VOLTAGE, new UnsignedWordElement(0x1578)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_122_VOLTAGE, new UnsignedWordElement(0x1579)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_123_VOLTAGE, new UnsignedWordElement(0x157A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_124_VOLTAGE, new UnsignedWordElement(0x157B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_125_VOLTAGE, new UnsignedWordElement(0x157C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_126_VOLTAGE, new UnsignedWordElement(0x157D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_127_VOLTAGE, new UnsignedWordElement(0x157E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_128_VOLTAGE, new UnsignedWordElement(0x157F)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_129_VOLTAGE, new UnsignedWordElement(0x1580)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_130_VOLTAGE, new UnsignedWordElement(0x1581)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_131_VOLTAGE, new UnsignedWordElement(0x1582)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_132_VOLTAGE, new UnsignedWordElement(0x1583)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_133_VOLTAGE, new UnsignedWordElement(0x1584)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_134_VOLTAGE, new UnsignedWordElement(0x1585)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_135_VOLTAGE, new UnsignedWordElement(0x1586)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_136_VOLTAGE, new UnsignedWordElement(0x1587)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_137_VOLTAGE, new UnsignedWordElement(0x1588)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_138_VOLTAGE, new UnsignedWordElement(0x1589)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_139_VOLTAGE, new UnsignedWordElement(0x158A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_140_VOLTAGE, new UnsignedWordElement(0x158B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_141_VOLTAGE, new UnsignedWordElement(0x158C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_142_VOLTAGE, new UnsignedWordElement(0x158D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_143_VOLTAGE, new UnsignedWordElement(0x158E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_144_VOLTAGE, new UnsignedWordElement(0x158F),
    -								ElementToChannelConverter.SCALE_FACTOR_1),
    -						m(EssFeneconCommercial40.ChannelId.CELL_145_VOLTAGE, new UnsignedWordElement(0x1590)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_146_VOLTAGE, new UnsignedWordElement(0x1591)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_147_VOLTAGE, new UnsignedWordElement(0x1592)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_148_VOLTAGE, new UnsignedWordElement(0x1593)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_149_VOLTAGE, new UnsignedWordElement(0x1594)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_150_VOLTAGE, new UnsignedWordElement(0x1595)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_151_VOLTAGE, new UnsignedWordElement(0x1596)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_152_VOLTAGE, new UnsignedWordElement(0x1597)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_153_VOLTAGE, new UnsignedWordElement(0x1598)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_154_VOLTAGE, new UnsignedWordElement(0x1599)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_155_VOLTAGE, new UnsignedWordElement(0x159A)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_156_VOLTAGE, new UnsignedWordElement(0x159B)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_157_VOLTAGE, new UnsignedWordElement(0x159C)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_158_VOLTAGE, new UnsignedWordElement(0x159D)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_159_VOLTAGE, new UnsignedWordElement(0x159E)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_160_VOLTAGE, new UnsignedWordElement(0x159F))),
    -				new FC3ReadRegistersTask(0x15A0, Priority.LOW,
    -						m(EssFeneconCommercial40.ChannelId.CELL_161_VOLTAGE, new UnsignedWordElement(0x15A0)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_162_VOLTAGE, new UnsignedWordElement(0x15A1)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_163_VOLTAGE, new UnsignedWordElement(0x15A2)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_164_VOLTAGE, new UnsignedWordElement(0x15A3)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_165_VOLTAGE, new UnsignedWordElement(0x15A4)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_166_VOLTAGE, new UnsignedWordElement(0x15A5)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_167_VOLTAGE, new UnsignedWordElement(0x15A6)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_168_VOLTAGE, new UnsignedWordElement(0x15A7)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_169_VOLTAGE, new UnsignedWordElement(0x15A8)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_170_VOLTAGE, new UnsignedWordElement(0x15A9)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_171_VOLTAGE, new UnsignedWordElement(0x15AA)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_172_VOLTAGE, new UnsignedWordElement(0x15AB)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_173_VOLTAGE, new UnsignedWordElement(0x15AC)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_174_VOLTAGE, new UnsignedWordElement(0x15AD)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_175_VOLTAGE, new UnsignedWordElement(0x15AE)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_176_VOLTAGE, new UnsignedWordElement(0x15AF)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_177_VOLTAGE, new UnsignedWordElement(0x15B0)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_178_VOLTAGE, new UnsignedWordElement(0x15B1)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_179_VOLTAGE, new UnsignedWordElement(0x15B2)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_180_VOLTAGE, new UnsignedWordElement(0x15B3)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_181_VOLTAGE, new UnsignedWordElement(0x15B4)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_182_VOLTAGE, new UnsignedWordElement(0x15B5)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_183_VOLTAGE, new UnsignedWordElement(0x15B6)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_184_VOLTAGE, new UnsignedWordElement(0x15B7)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_185_VOLTAGE, new UnsignedWordElement(0x15B8)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_186_VOLTAGE, new UnsignedWordElement(0x15B9)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_187_VOLTAGE, new UnsignedWordElement(0x15BA)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_188_VOLTAGE, new UnsignedWordElement(0x15BB)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_189_VOLTAGE, new UnsignedWordElement(0x15BC)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_190_VOLTAGE, new UnsignedWordElement(0x15BD)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_191_VOLTAGE, new UnsignedWordElement(0x15BE)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_192_VOLTAGE, new UnsignedWordElement(0x15BF)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_193_VOLTAGE, new UnsignedWordElement(0x15C0)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_194_VOLTAGE, new UnsignedWordElement(0x15C1)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_195_VOLTAGE, new UnsignedWordElement(0x15C2)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_196_VOLTAGE, new UnsignedWordElement(0x15C3)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_197_VOLTAGE, new UnsignedWordElement(0x15C4)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_198_VOLTAGE, new UnsignedWordElement(0x15C5)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_199_VOLTAGE, new UnsignedWordElement(0x15C6)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_200_VOLTAGE, new UnsignedWordElement(0x15C7)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_201_VOLTAGE, new UnsignedWordElement(0x15C8)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_202_VOLTAGE, new UnsignedWordElement(0x15C9)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_203_VOLTAGE, new UnsignedWordElement(0x15CA)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_204_VOLTAGE, new UnsignedWordElement(0x15CB)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_205_VOLTAGE, new UnsignedWordElement(0x15CC)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_206_VOLTAGE, new UnsignedWordElement(0x15CD)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_207_VOLTAGE, new UnsignedWordElement(0x15CE)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_208_VOLTAGE, new UnsignedWordElement(0x15CF)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_209_VOLTAGE, new UnsignedWordElement(0x15D0)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_210_VOLTAGE, new UnsignedWordElement(0x15D1)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_211_VOLTAGE, new UnsignedWordElement(0x15D2)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_212_VOLTAGE, new UnsignedWordElement(0x15D3)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_213_VOLTAGE, new UnsignedWordElement(0x15D4)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_214_VOLTAGE, new UnsignedWordElement(0x15D5)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_215_VOLTAGE, new UnsignedWordElement(0x15D6)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_216_VOLTAGE, new UnsignedWordElement(0x15D7)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_217_VOLTAGE, new UnsignedWordElement(0x15D8)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_218_VOLTAGE, new UnsignedWordElement(0x15D9)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_219_VOLTAGE, new UnsignedWordElement(0x15DA)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_220_VOLTAGE, new UnsignedWordElement(0x15DB)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_221_VOLTAGE, new UnsignedWordElement(0x15DC)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_222_VOLTAGE, new UnsignedWordElement(0x15DD)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_223_VOLTAGE, new UnsignedWordElement(0x15DE)),
    -						m(EssFeneconCommercial40.ChannelId.CELL_224_VOLTAGE, new UnsignedWordElement(0x15DF))),
    -				new FC3ReadRegistersTask(0x1402, Priority.HIGH, //
    -						m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(0x1402))));
    -	}
    -
    -	@Override
    -	public String debugLog() {
    -		return "SoC:" + this.getSoc().value().asString() //
    -				+ "|L:" + this.getActivePower().value().asString() //
    -				+ "|Allowed:"
    -				+ this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER).value().asStringWithoutUnit() + ";"
    -				+ this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER).value().asString() //
    -				+ "|" + this.getGridMode().value().asOptionString();
    -	}
    -
    -	@Override
    -	public void handleEvent(Event event) {
    -		if (!this.isEnabled()) {
    -			return;
    -		}
    -		switch (event.getTopic()) {
    -		case EdgeEventConstants.TOPIC_CYCLE_BEFORE_CONTROLLERS:
    -			this.defineWorkState();
    -			break;
    -		}
    -	}
    -
    -	private LocalDateTime lastDefineWorkState = null;
    -
    -	private void defineWorkState() {
    -		/*
    -		 * Set ESS in running mode
    -		 */
    -		// TODO this should be smarter: set in energy saving mode if there was no output
    -		// power for a while and we don't need emergency power.
    -		LocalDateTime now = LocalDateTime.now();
    -		if (lastDefineWorkState == null || now.minusMinutes(1).isAfter(this.lastDefineWorkState)) {
    -			this.lastDefineWorkState = now;
    -			EnumWriteChannel setWorkStateChannel = this.channel(ChannelId.SET_WORK_STATE);
    -			try {
    -				setWorkStateChannel.setNextWriteValue(SetWorkState.START);
    -			} catch (OpenemsException e) {
    -				logError(this.log, "Unable to start: " + e.getMessage());
    -			}
    -		}
    -	}
    -
    -	@Override
    -	public Power getPower() {
    -		return this.power;
    -	}
     
    -	@Override
    -	public int getPowerPrecision() {
    -		return 100; // the modbus field for SetActivePower has the unit 0.1 kW
    -	}
    +public interface EssFeneconCommercial40
    +		extends ManagedSymmetricEss, SymmetricEss, OpenemsComponent, EventHandler, ModbusSlave {
     
    -	@Override
    -	public Constraint[] getStaticConstraints() {
    -		if (this.readOnlyMode) {
    -			return new Constraint[] { //
    -					this.createPowerConstraint("Read-Only-Mode", Phase.ALL, Pwr.ACTIVE, Relationship.EQUALS, 0), //
    -					this.createPowerConstraint("Read-Only-Mode", Phase.ALL, Pwr.REACTIVE, Relationship.EQUALS, 0) //
    -			};
    -		} else {
    -			return new Constraint[] {
    -					// ReactivePower limitations
    -					this.createPowerConstraint("Commercial40 Min Reactive Power", Phase.ALL, Pwr.REACTIVE,
    -							Relationship.GREATER_OR_EQUALS, MIN_REACTIVE_POWER),
    -					this.createPowerConstraint("Commercial40 Max Reactive Power", Phase.ALL, Pwr.REACTIVE,
    -							Relationship.LESS_OR_EQUALS, MAX_REACTIVE_POWER) };
    -		}
    -	}
    +	public Integer getUnitId();
     
    -	@Override
    -	public ModbusSlaveTable getModbusSlaveTable() {
    -		return new ModbusSlaveTable( //
    -				OpenemsComponent.getModbusSlaveNatureTable(), //
    -				SymmetricEss.getModbusSlaveNatureTable(), //
    -				ManagedSymmetricEss.getModbusSlaveNatureTable() //
    -		);
    -	}
    +	public String getModbusBridgeId();
     
    -}
    \ No newline at end of file
    +}
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java
    new file mode 100644
    index 00000000000..735731d8714
    --- /dev/null
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java
    @@ -0,0 +1,1599 @@
    +package io.openems.edge.ess.fenecon.commercial40;
    +
    +import java.time.LocalDateTime;
    +import java.util.Optional;
    +
    +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.event.Event;
    +import org.osgi.service.event.EventConstants;
    +import org.osgi.service.event.EventHandler;
    +import org.osgi.service.metatype.annotations.Designate;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Level;
    +import io.openems.common.channel.Unit;
    +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.types.OpenemsType;
    +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
    +import io.openems.edge.bridge.modbus.api.BridgeModbus;
    +import io.openems.edge.bridge.modbus.api.ElementToChannelConverter;
    +import io.openems.edge.bridge.modbus.api.ModbusProtocol;
    +import io.openems.edge.bridge.modbus.api.element.BitsWordElement;
    +import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement;
    +import io.openems.edge.bridge.modbus.api.element.SignedWordElement;
    +import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement;
    +import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement;
    +import io.openems.edge.bridge.modbus.api.element.WordOrder;
    +import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask;
    +import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
    +import io.openems.edge.common.channel.Doc;
    +import io.openems.edge.common.channel.EnumWriteChannel;
    +import io.openems.edge.common.channel.IntegerDoc;
    +import io.openems.edge.common.channel.IntegerReadChannel;
    +import io.openems.edge.common.channel.IntegerWriteChannel;
    +import io.openems.edge.common.component.OpenemsComponent;
    +import io.openems.edge.common.event.EdgeEventConstants;
    +import io.openems.edge.common.modbusslave.ModbusSlave;
    +import io.openems.edge.common.modbusslave.ModbusSlaveTable;
    +import io.openems.edge.common.sum.GridMode;
    +import io.openems.edge.common.taskmanager.Priority;
    +import io.openems.edge.common.type.TypeUtils;
    +import io.openems.edge.ess.api.ManagedSymmetricEss;
    +import io.openems.edge.ess.api.SymmetricEss;
    +import io.openems.edge.ess.power.api.Constraint;
    +import io.openems.edge.ess.power.api.Phase;
    +import io.openems.edge.ess.power.api.Power;
    +import io.openems.edge.ess.power.api.Pwr;
    +import io.openems.edge.ess.power.api.Relationship;
    +
    +@Designate(ocd = Config.class, factory = true)
    +@Component( //
    +		name = "Ess.Fenecon.Commercial40", //
    +		immediate = true, //
    +		configurationPolicy = ConfigurationPolicy.REQUIRE, //
    +		property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_CONTROLLERS //
    +)
    +public class EssFeneconCommercial40Impl extends AbstractOpenemsModbusComponent implements EssFeneconCommercial40,
    +		ManagedSymmetricEss, SymmetricEss, OpenemsComponent, EventHandler, ModbusSlave {
    +
    +	private final Logger log = LoggerFactory.getLogger(EssFeneconCommercial40Impl.class);
    +
    +	protected final static int MAX_APPARENT_POWER = 40000;
    +
    +	private final static int UNIT_ID = 100;
    +	private final static int MIN_REACTIVE_POWER = -10000;
    +	private final static int MAX_REACTIVE_POWER = 10000;
    +
    +	private String modbusBridgeId;
    +	private boolean readOnlyMode = false;
    +
    +	@Reference
    +	private Power power;
    +
    +	@Reference
    +	protected ConfigurationAdmin cm;
    +
    +	public EssFeneconCommercial40Impl() {
    +		super(//
    +				OpenemsComponent.ChannelId.values(), //
    +				SymmetricEss.ChannelId.values(), //
    +				ManagedSymmetricEss.ChannelId.values(), //
    +				ChannelId.values() //
    +		);
    +	}
    +
    +	@Override
    +	public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException {
    +		if (this.readOnlyMode) {
    +			return;
    +		}
    +
    +		IntegerWriteChannel setActivePowerChannel = this.channel(ChannelId.SET_ACTIVE_POWER);
    +		setActivePowerChannel.setNextWriteValue(activePower);
    +		IntegerWriteChannel setReactivePowerChannel = this.channel(ChannelId.SET_REACTIVE_POWER);
    +		setReactivePowerChannel.setNextWriteValue(reactivePower);
    +	}
    +
    +	@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
    +	protected void setModbus(BridgeModbus modbus) {
    +		super.setModbus(modbus);
    +	}
    +
    +	@Activate
    +	void activate(ComponentContext context, Config config) {
    +		super.activate(context, config.id(), config.alias(), config.enabled(), UNIT_ID, this.cm, "Modbus",
    +				config.modbus_id());
    +		this.modbusBridgeId = config.modbus_id();
    +		this.readOnlyMode = config.readOnlyMode();
    +	}
    +
    +	@Deactivate
    +	protected void deactivate() {
    +		super.deactivate();
    +	}
    +
    +	public String getModbusBridgeId() {
    +		return modbusBridgeId;
    +	}
    +
    +	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
    +		// EnumReadChannels
    +		SYSTEM_STATE(Doc.of(SystemState.values())), //
    +		CONTROL_MODE(Doc.of(ControlMode.values())), //
    +		BATTERY_MAINTENANCE_STATE(Doc.of(BatteryMaintenanceState.values())), //
    +		INVERTER_STATE(Doc.of(InverterState.values())), //
    +		SYSTEM_MANUFACTURER(Doc.of(SystemManufacturer.values())), //
    +		SYSTEM_TYPE(Doc.of(SystemType.values())), //
    +		BATTERY_STRING_SWITCH_STATE(Doc.of(BatteryStringSwitchState.values())), //
    +		BMS_DCDC_WORK_STATE(Doc.of(BmsDcdcWorkState.values())), //
    +		BMS_DCDC_WORK_MODE(Doc.of(BmsDcdcWorkMode.values())), //
    +
    +		// EnumWriteChannels
    +		SET_WORK_STATE(Doc.of(SetWorkState.values()) //
    +				.accessMode(AccessMode.WRITE_ONLY)), //
    +
    +		// IntegerWriteChannel
    +		SET_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.WATT) //
    +				.accessMode(AccessMode.WRITE_ONLY)), //
    +		SET_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.VOLT_AMPERE_REACTIVE) //
    +				.accessMode(AccessMode.WRITE_ONLY)), //
    +		SET_PV_POWER_LIMIT(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.WATT) //
    +				.accessMode(AccessMode.WRITE_ONLY)), //
    +
    +		// IntegerReadChannels
    +		ORIGINAL_ALLOWED_CHARGE_POWER(new IntegerDoc() //
    +				.onInit(channel -> { //
    +					// on each Update to the channel -> set the ALLOWED_CHARGE_POWER value with a
    +					// delta of max 500
    +					channel.onChange(originalValueChannel -> {
    +						IntegerReadChannel currentValueChannel = channel.getComponent()
    +								.channel(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER);
    +						Optional originalValue = originalValueChannel.asOptional();
    +						Optional currentValue = currentValueChannel.value().asOptional();
    +						int value;
    +						if (!originalValue.isPresent() && !currentValue.isPresent()) {
    +							value = 0;
    +						} else if (originalValue.isPresent() && !currentValue.isPresent()) {
    +							value = originalValue.get();
    +						} else if (!originalValue.isPresent() && currentValue.isPresent()) {
    +							value = currentValue.get();
    +						} else {
    +							value = Math.max(originalValue.get(), currentValue.get() - 500);
    +						}
    +						currentValueChannel.setNextValue(value);
    +					});
    +				})), //
    +		ORIGINAL_ALLOWED_DISCHARGE_POWER(new IntegerDoc() //
    +				.onInit(channel -> { //
    +					// on each Update to the channel -> set the ALLOWED_DISCHARGE_POWER value with a
    +					// delta of max 500
    +					channel.onChange(originalValueChannel -> {
    +						IntegerReadChannel currentValueChannel = channel.getComponent()
    +								.channel(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER);
    +						Optional originalValue = originalValueChannel.asOptional();
    +						Optional currentValue = currentValueChannel.value().asOptional();
    +						int value;
    +						if (!originalValue.isPresent() && !currentValue.isPresent()) {
    +							value = 0;
    +						} else if (originalValue.isPresent() && !currentValue.isPresent()) {
    +							value = originalValue.get();
    +						} else if (!originalValue.isPresent() && currentValue.isPresent()) {
    +							value = currentValue.get();
    +						} else {
    +							value = Math.min(originalValue.get(), currentValue.get() + 500);
    +						}
    +						currentValueChannel.setNextValue(value);
    +					});
    +				})), //
    +		PROTOCOL_VERSION(Doc.of(OpenemsType.INTEGER)), //
    +		BATTERY_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		BATTERY_CURRENT(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIAMPERE)), //
    +		BATTERY_POWER(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.WATT)), //
    +		AC_CHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.WATT_HOURS)), //
    +		AC_DISCHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.WATT_HOURS)), //
    +		GRID_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.WATT)), //
    +		APPARENT_POWER(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.VOLT_AMPERE)), //
    +		CURRENT_L1(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIAMPERE)), //
    +		CURRENT_L2(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIAMPERE)), //
    +		CURRENT_L3(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIAMPERE)), //
    +		VOLTAGE_L1(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		VOLTAGE_L2(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		VOLTAGE_L3(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		FREQUENCY(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIHERTZ)), //
    +		INVERTER_VOLTAGE_L1(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		INVERTER_VOLTAGE_L2(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		INVERTER_VOLTAGE_L3(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		INVERTER_CURRENT_L1(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIAMPERE)), //
    +		INVERTER_CURRENT_L2(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIAMPERE)), //
    +		INVERTER_CURRENT_L3(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIAMPERE)), //
    +		IPM_TEMPERATURE_L1(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.DEGREE_CELSIUS)), //
    +		IPM_TEMPERATURE_L2(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.DEGREE_CELSIUS)), //
    +		IPM_TEMPERATURE_L3(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.DEGREE_CELSIUS)), //
    +		TRANSFORMER_TEMPERATURE_L2(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.DEGREE_CELSIUS)), //
    +		CELL_1_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_2_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_3_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_4_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_5_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_6_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_7_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_8_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_9_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_10_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_11_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_12_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_13_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_14_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_15_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_16_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_17_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_18_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_19_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_20_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_21_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_22_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_23_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_24_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_25_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_26_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_27_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_28_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_29_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_30_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_31_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_32_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_33_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_34_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_35_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_36_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_37_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_38_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_39_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_40_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_41_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_42_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_43_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_44_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_45_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_46_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_47_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_48_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_49_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_50_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_51_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_52_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_53_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_54_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_55_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_56_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_57_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_58_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_59_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_60_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_61_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_62_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_63_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_64_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_65_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_66_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_67_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_68_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_69_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_70_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_71_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_72_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_73_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_74_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_75_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_76_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_77_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_78_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_79_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_80_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_81_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_82_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_83_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_84_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_85_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_86_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_87_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_88_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_89_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_90_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_91_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_92_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_93_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_94_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_95_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_96_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_97_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_98_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_99_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_100_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_101_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_102_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_103_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_104_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_105_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_106_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_107_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_108_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_109_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_110_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_111_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_112_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_113_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_114_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_115_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_116_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_117_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_118_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_119_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_120_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_121_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_122_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_123_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_124_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_125_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_126_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_127_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_128_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_129_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_130_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_131_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_132_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_133_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_134_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_135_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_136_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_137_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_138_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_139_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_140_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_141_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_142_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_143_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_144_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_145_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_146_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_147_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_148_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_149_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_150_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_151_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_152_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_153_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_154_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_155_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_156_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_157_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_158_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_159_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_160_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_161_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_162_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_163_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_164_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_165_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_166_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_167_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_168_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_169_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_170_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_171_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_172_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_173_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_174_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_175_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_176_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_177_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_178_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_179_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_180_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_181_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_182_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_183_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_184_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_185_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_186_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_187_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_188_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_189_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_190_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_191_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_192_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_193_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_194_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_195_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_196_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_197_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_198_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_199_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_200_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_201_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_202_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_203_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_204_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_205_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_206_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_207_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_208_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_209_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_210_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_211_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_212_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_213_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_214_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_215_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_216_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_217_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_218_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_219_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_220_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_221_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_222_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_223_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +		CELL_224_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
    +				.unit(Unit.MILLIVOLT)), //
    +
    +		// StateChannels
    +		STATE_0(Doc.of(Level.WARNING) //
    +				.text("Emergency Stop")), //
    +		STATE_1(Doc.of(Level.WARNING) //
    +				.text("Key Manual Stop")), //
    +		STATE_2(Doc.of(Level.WARNING) //
    +				.text("Transformer Phase B Temperature Sensor Invalidation")), //
    +		STATE_3(Doc.of(Level.WARNING) //
    +				.text("SD Memory Card Invalidation")), //
    +		STATE_4(Doc.of(Level.WARNING) //
    +				.text("Inverter Communication Abnormity")), //
    +		STATE_5(Doc.of(Level.WARNING) //
    +				.text("Battery Stack Communication Abnormity")), //
    +		STATE_6(Doc.of(Level.WARNING) //
    +				.text("Multifunctional Ammeter Communication Abnormity")), //
    +		STATE_7(Doc.of(Level.WARNING) //
    +				.text("Remote Communication Abnormity")), //
    +		STATE_8(Doc.of(Level.WARNING) //
    +				.text("PVDC1 Communication Abnormity")), //
    +		STATE_9(Doc.of(Level.WARNING) //
    +				.text("PVDC2 Communication Abnormity")), //
    +		STATE_10(Doc.of(Level.WARNING) //
    +				.text("Transformer Severe Overtemperature")), //
    +		STATE_11(Doc.of(Level.FAULT) //
    +				.text("DC Precharge Contactor Close Unsuccessfully")), //
    +		STATE_12(Doc.of(Level.FAULT) //
    +				.text("AC Precharge Contactor Close Unsuccessfully")), //
    +		STATE_13(Doc.of(Level.FAULT) //
    +				.text("AC Main Contactor Close Unsuccessfully")), //
    +		STATE_14(Doc.of(Level.FAULT) //
    +				.text("DC Electrical Breaker1 Close Unsuccessfully")), //
    +		STATE_15(Doc.of(Level.FAULT) //
    +				.text("DC Main Contactor Close Unsuccessfully")), //
    +		STATE_16(Doc.of(Level.FAULT) //
    +				.text("AC Breaker Trip")), //
    +		STATE_17(Doc.of(Level.FAULT) //
    +				.text("AC Main Contactor Open When Running")), //
    +		STATE_18(Doc.of(Level.FAULT) //
    +				.text("DC Main Contactor Open When Running")), //
    +		STATE_19(Doc.of(Level.FAULT) //
    +				.text("AC Main Contactor Open Unsuccessfully")), //
    +		STATE_20(Doc.of(Level.FAULT) //
    +				.text("DC Electrical Breaker1 Open Unsuccessfully")), //
    +		STATE_21(Doc.of(Level.FAULT) //
    +				.text("DC Main Contactor Open Unsuccessfully")), //
    +		STATE_22(Doc.of(Level.FAULT) //
    +				.text("Hardware PDP Fault")), //
    +		STATE_23(Doc.of(Level.FAULT) //
    +				.text("Master Stop Suddenly")), //
    +		STATE_24(Doc.of(Level.FAULT) //
    +				.text("DCShortCircuitProtection")), //
    +		STATE_25(Doc.of(Level.FAULT) //
    +				.text("DCOvervoltageProtection")), //
    +		STATE_26(Doc.of(Level.FAULT) //
    +				.text("DCUndervoltageProtection")), //
    +		STATE_27(Doc.of(Level.FAULT) //
    +				.text("DCInverseNoConnectionProtection")), //
    +		STATE_28(Doc.of(Level.FAULT) //
    +				.text("DCDisconnectionProtection")), //
    +		STATE_29(Doc.of(Level.FAULT) //
    +				.text("CommutingVoltageAbnormityProtection")), //
    +		STATE_30(Doc.of(Level.FAULT) //
    +				.text("DCOvercurrentProtection")), //
    +		STATE_31(Doc.of(Level.FAULT) //
    +				.text("Phase1PeakCurrentOverLimitProtection")), //
    +		STATE_32(Doc.of(Level.FAULT) //
    +				.text("Phase2PeakCurrentOverLimitProtection")), //
    +		STATE_33(Doc.of(Level.FAULT) //
    +				.text("Phase3PeakCurrentOverLimitProtection")), //
    +		STATE_34(Doc.of(Level.FAULT) //
    +				.text("Phase1GridVoltageSamplingInvalidation")), //
    +		STATE_35(Doc.of(Level.FAULT) //
    +				.text("Phase2VirtualCurrentOverLimitProtection")), //
    +		STATE_36(Doc.of(Level.FAULT) //
    +				.text("Phase3VirtualCurrentOverLimitProtection")), //
    +		STATE_37(Doc.of(Level.FAULT) //
    +				.text("Phase1GridVoltageSamplingInvalidation2")), //
    +		STATE_38(Doc.of(Level.FAULT) //
    +				.text("Phase2ridVoltageSamplingInvalidation")), //
    +		STATE_39(Doc.of(Level.FAULT) //
    +				.text("Phase3GridVoltageSamplingInvalidation")), //
    +		STATE_40(Doc.of(Level.FAULT) //
    +				.text("Phase1InvertVoltageSamplingInvalidation")), //
    +		STATE_41(Doc.of(Level.FAULT) //
    +				.text("Phase2InvertVoltageSamplingInvalidation")), //
    +		STATE_42(Doc.of(Level.FAULT) //
    +				.text("Phase3InvertVoltageSamplingInvalidation")), //
    +		STATE_43(Doc.of(Level.FAULT) //
    +				.text("ACCurrentSamplingInvalidation")), //
    +		STATE_44(Doc.of(Level.FAULT) //
    +				.text("DCCurrentSamplingInvalidation")), //
    +		STATE_45(Doc.of(Level.FAULT) //
    +				.text("Phase1OvertemperatureProtection")), //
    +		STATE_46(Doc.of(Level.FAULT) //
    +				.text("Phase2OvertemperatureProtection")), //
    +		STATE_47(Doc.of(Level.FAULT) //
    +				.text("Phase3OvertemperatureProtection")), //
    +		STATE_48(Doc.of(Level.FAULT) //
    +				.text("Phase1TemperatureSamplingInvalidation")), //
    +		STATE_49(Doc.of(Level.FAULT) //
    +				.text("Phase2TemperatureSamplingInvalidation")), //
    +		STATE_50(Doc.of(Level.FAULT) //
    +				.text("Phase3TemperatureSamplingInvalidation")), //
    +		STATE_51(Doc.of(Level.FAULT) //
    +				.text("Phase1PrechargeUnmetProtection")), //
    +		STATE_52(Doc.of(Level.FAULT) //
    +				.text("Phase2PrechargeUnmetProtection")), //
    +		STATE_53(Doc.of(Level.FAULT) //
    +				.text("Phase3PrechargeUnmetProtection")), //
    +		STATE_54(Doc.of(Level.FAULT) //
    +				.text("UnadaptablePhaseSequenceErrorProtection")), //
    +		STATE_55(Doc.of(Level.FAULT) //
    +				.text("DSPProtection")), //
    +		STATE_56(Doc.of(Level.FAULT) //
    +				.text("Phase1GridVoltageSevereOvervoltageProtection")), //
    +		STATE_57(Doc.of(Level.FAULT) //
    +				.text("Phase1GridVoltageGeneralOvervoltageProtection")), //
    +		STATE_58(Doc.of(Level.FAULT) //
    +				.text("Phase2GridVoltageSevereOvervoltageProtection")), //
    +		STATE_59(Doc.of(Level.FAULT) //
    +				.text("Phase2GridVoltageGeneralOvervoltageProtection")), //
    +		STATE_60(Doc.of(Level.FAULT) //
    +				.text("Phase3GridVoltageSevereOvervoltageProtection")), //
    +		STATE_61(Doc.of(Level.FAULT) //
    +				.text("Phase3GridVoltageGeneralOvervoltageProtection")), //
    +		STATE_62(Doc.of(Level.FAULT) //
    +				.text("Phase1GridVoltageSevereUndervoltageProtection")), //
    +		STATE_63(Doc.of(Level.FAULT) //
    +				.text("Phase1GridVoltageGeneralUndervoltageProtection")), //
    +		STATE_64(Doc.of(Level.FAULT) //
    +				.text("Phase2GridVoltageSevereUndervoltageProtection")), //
    +		STATE_65(Doc.of(Level.FAULT) //
    +				.text("Phase2GridVoltageGeneralUndervoltageProtection")), //
    +		STATE_66(Doc.of(Level.FAULT) //
    +				.text("Phase3GridVoltageSevereUndervoltageProtection")), //
    +		STATE_67(Doc.of(Level.FAULT) //
    +				.text("Phase3GridVoltageGeneralUndervoltageProtection")), //
    +		STATE_68(Doc.of(Level.FAULT) //
    +				.text("SevereOverfrequncyProtection")), //
    +		STATE_69(Doc.of(Level.FAULT) //
    +				.text("GeneralOverfrequncyProtection")), //
    +		STATE_70(Doc.of(Level.FAULT) //
    +				.text("SevereUnderfrequncyProtection")), //
    +		STATE_71(Doc.of(Level.FAULT) //
    +				.text("GeneralsUnderfrequncyProtection")), //
    +		STATE_72(Doc.of(Level.FAULT) //
    +				.text("Phase1Gridloss")), //
    +		STATE_73(Doc.of(Level.FAULT) //
    +				.text("Phase2Gridloss")), //
    +		STATE_74(Doc.of(Level.FAULT) //
    +				.text("Phase3Gridloss")), //
    +		STATE_75(Doc.of(Level.FAULT) //
    +				.text("IslandingProtection")), //
    +		STATE_76(Doc.of(Level.FAULT) //
    +				.text("Phase1UnderVoltageRideThrough")), //
    +		STATE_77(Doc.of(Level.FAULT) //
    +				.text("Phase2UnderVoltageRideThrough")), //
    +		STATE_78(Doc.of(Level.FAULT) //
    +				.text("Phase3UnderVoltageRideThrough")), //
    +		STATE_79(Doc.of(Level.FAULT) //
    +				.text("Phase1InverterVoltageSevereOvervoltageProtection")), //
    +		STATE_80(Doc.of(Level.FAULT) //
    +				.text("Phase1InverterVoltageGeneralOvervoltageProtection")), //
    +		STATE_81(Doc.of(Level.FAULT) //
    +				.text("Phase2InverterVoltageSevereOvervoltageProtection")), //
    +		STATE_82(Doc.of(Level.FAULT) //
    +				.text("Phase2InverterVoltageGeneralOvervoltageProtection")), //
    +		STATE_83(Doc.of(Level.FAULT) //
    +				.text("Phase3InverterVoltageSevereOvervoltageProtection")), //
    +		STATE_84(Doc.of(Level.FAULT) //
    +				.text("Phase3InverterVoltageGeneralOvervoltageProtection")), //
    +		STATE_85(Doc.of(Level.FAULT) //
    +				.text("InverterPeakVoltageHighProtectionCauseByACDisconnect")), //
    +		STATE_86(Doc.of(Level.WARNING) //
    +				.text("DCPrechargeContactorInspectionAbnormity")), //
    +		STATE_87(Doc.of(Level.WARNING) //
    +				.text("DCBreaker1InspectionAbnormity")), //
    +		STATE_88(Doc.of(Level.WARNING) //
    +				.text("DCBreaker2InspectionAbnormity")), //
    +		STATE_89(Doc.of(Level.WARNING) //
    +				.text("ACPrechargeContactorInspectionAbnormity")), //
    +		STATE_90(Doc.of(Level.WARNING) //
    +				.text("ACMainontactorInspectionAbnormity")), //
    +		STATE_91(Doc.of(Level.WARNING) //
    +				.text("ACBreakerInspectionAbnormity")), //
    +		STATE_92(Doc.of(Level.WARNING) //
    +				.text("DCBreaker1CloseUnsuccessfully")), //
    +		STATE_93(Doc.of(Level.WARNING) //
    +				.text("DCBreaker2CloseUnsuccessfully")), //
    +		STATE_94(Doc.of(Level.WARNING) //
    +				.text("ControlSignalCloseAbnormallyInspectedBySystem")), //
    +		STATE_95(Doc.of(Level.WARNING) //
    +				.text("ControlSignalOpenAbnormallyInspectedBySystem")), //
    +		STATE_96(Doc.of(Level.WARNING) //
    +				.text("NeutralWireContactorCloseUnsuccessfully")), //
    +		STATE_97(Doc.of(Level.WARNING) //
    +				.text("NeutralWireContactorOpenUnsuccessfully")), //
    +		STATE_98(Doc.of(Level.WARNING) //
    +				.text("WorkDoorOpen")), //
    +		STATE_99(Doc.of(Level.WARNING) //
    +				.text("Emergency1Stop")), //
    +		STATE_100(Doc.of(Level.WARNING) //
    +				.text("ACBreakerCloseUnsuccessfully")), //
    +		STATE_101(Doc.of(Level.WARNING) //
    +				.text("ControlSwitchStop")), //
    +		STATE_102(Doc.of(Level.WARNING) //
    +				.text("GeneralOverload")), //
    +		STATE_103(Doc.of(Level.WARNING) //
    +				.text("SevereOverload")), //
    +		STATE_104(Doc.of(Level.WARNING) //
    +				.text("BatteryCurrentOverLimit")), //
    +		STATE_105(Doc.of(Level.WARNING) //
    +				.text("PowerDecreaseCausedByOvertemperature")), //
    +		STATE_106(Doc.of(Level.WARNING) //
    +				.text("InverterGeneralOvertemperature")), //
    +		STATE_107(Doc.of(Level.WARNING) //
    +				.text("ACThreePhaseCurrentUnbalance")), //
    +		STATE_108(Doc.of(Level.WARNING) //
    +				.text("RestoreFactorySettingUnsuccessfully")), //
    +		STATE_109(Doc.of(Level.WARNING) //
    +				.text("PoleBoardInvalidation")), //
    +		STATE_110(Doc.of(Level.WARNING) //
    +				.text("SelfInspectionFailed")), //
    +		STATE_111(Doc.of(Level.WARNING) //
    +				.text("ReceiveBMSFaultAndStop")), //
    +		STATE_112(Doc.of(Level.WARNING) //
    +				.text("RefrigerationEquipmentinvalidation")), //
    +		STATE_113(Doc.of(Level.WARNING) //
    +				.text("LargeTemperatureDifferenceAmongIGBTThreePhases")), //
    +		STATE_114(Doc.of(Level.WARNING) //
    +				.text("EEPROMParametersOverRange")), //
    +		STATE_115(Doc.of(Level.WARNING) //
    +				.text("EEPROMParametersBackupFailed")), //
    +		STATE_116(Doc.of(Level.WARNING) //
    +				.text("DCBreakerCloseunsuccessfully")), //
    +		STATE_117(Doc.of(Level.WARNING) //
    +				.text("CommunicationBetweenInverterAndBSMUDisconnected")), //
    +		STATE_118(Doc.of(Level.WARNING) //
    +				.text("CommunicationBetweenInverterAndMasterDisconnected")), //
    +		STATE_119(Doc.of(Level.WARNING) //
    +				.text("CommunicationBetweenInverterAndUCDisconnected")), //
    +		STATE_120(Doc.of(Level.WARNING) //
    +				.text("BMSStartOvertimeControlledByPCS")), //
    +		STATE_121(Doc.of(Level.WARNING) //
    +				.text("BMSStopOvertimeControlledByPCS")), //
    +		STATE_122(Doc.of(Level.WARNING) //
    +				.text("SyncSignalInvalidation")), //
    +		STATE_123(Doc.of(Level.WARNING) //
    +				.text("SyncSignalContinuousCaputureFault")), //
    +		STATE_124(Doc.of(Level.WARNING) //
    +				.text("SyncSignalSeveralTimesCaputureFault")), //
    +		STATE_125(Doc.of(Level.WARNING) //
    +				.text("CurrentSamplingChannelAbnormityOnHighVoltageSide")), //
    +		STATE_126(Doc.of(Level.WARNING) //
    +				.text("CurrentSamplingChannelAbnormityOnLowVoltageSide")), //
    +		STATE_127(Doc.of(Level.WARNING) //
    +				.text("EEPROMParametersOverRange")), //
    +		STATE_128(Doc.of(Level.WARNING) //
    +				.text("UpdateEEPROMFailed")), //
    +		STATE_129(Doc.of(Level.WARNING) //
    +				.text("ReadEEPROMFailed")), //
    +		STATE_130(Doc.of(Level.WARNING) //
    +				.text("CurrentSamplingChannelAbnormityBeforeInductance")), //
    +		STATE_131(Doc.of(Level.WARNING) //
    +				.text("ReactorPowerDecreaseCausedByOvertemperature")), //
    +		STATE_132(Doc.of(Level.WARNING) //
    +				.text("IGBTPowerDecreaseCausedByOvertemperature")), //
    +		STATE_133(Doc.of(Level.WARNING) //
    +				.text("TemperatureChanel3PowerDecreaseCausedByOvertemperature")), //
    +		STATE_134(Doc.of(Level.WARNING) //
    +				.text("TemperatureChanel4PowerDecreaseCausedByOvertemperature")), //
    +		STATE_135(Doc.of(Level.WARNING) //
    +				.text("TemperatureChanel5PowerDecreaseCausedByOvertemperature")), //
    +		STATE_136(Doc.of(Level.WARNING) //
    +				.text("TemperatureChanel6PowerDecreaseCausedByOvertemperature")), //
    +		STATE_137(Doc.of(Level.WARNING) //
    +				.text("TemperatureChanel7PowerDecreaseCausedByOvertemperature")), //
    +		STATE_138(Doc.of(Level.WARNING) //
    +				.text("TemperatureChanel8PowerDecreaseCausedByOvertemperature")), //
    +		STATE_139(Doc.of(Level.WARNING) //
    +				.text("Fan1StopFailed")), //
    +		STATE_140(Doc.of(Level.WARNING) //
    +				.text("Fan2StopFailed")), //
    +		STATE_141(Doc.of(Level.WARNING) //
    +				.text("Fan3StopFailed")), //
    +		STATE_142(Doc.of(Level.WARNING) //
    +				.text("Fan4StopFailed")), //
    +		STATE_143(Doc.of(Level.WARNING) //
    +				.text("Fan1StartupFailed")), //
    +		STATE_144(Doc.of(Level.WARNING) //
    +				.text("Fan2StartupFailed")), //
    +		STATE_145(Doc.of(Level.WARNING) //
    +				.text("Fan3StartupFailed")), //
    +		STATE_146(Doc.of(Level.WARNING) //
    +				.text("Fan4StartupFailed")), //
    +		STATE_147(Doc.of(Level.WARNING) //
    +				.text("HighVoltageSideOvervoltage")), //
    +		STATE_148(Doc.of(Level.WARNING) //
    +				.text("HighVoltageSideUndervoltage")), //
    +		STATE_149(Doc.of(Level.WARNING) //
    +				.text("HighVoltageSideVoltageChangeUnconventionally")); //
    +
    +		private final Doc doc;
    +
    +		private ChannelId(Doc doc) {
    +			this.doc = doc;
    +		}
    +
    +		@Override
    +		public Doc doc() {
    +			return this.doc;
    +		}
    +	}
    +
    +	@Override
    +	protected ModbusProtocol defineModbusProtocol() {
    +		return new ModbusProtocol(this, //
    +				new FC3ReadRegistersTask(0x0101, Priority.LOW, //
    +						m(EssFeneconCommercial40Impl.ChannelId.SYSTEM_STATE, new UnsignedWordElement(0x0101)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CONTROL_MODE, new UnsignedWordElement(0x0102)),
    +						new DummyRegisterElement(0x0103), // WorkMode: RemoteDispatch
    +						m(EssFeneconCommercial40Impl.ChannelId.BATTERY_MAINTENANCE_STATE,
    +								new UnsignedWordElement(0x0104)),
    +						m(EssFeneconCommercial40Impl.ChannelId.INVERTER_STATE, new UnsignedWordElement(0x0105)),
    +						m(SymmetricEss.ChannelId.GRID_MODE, new UnsignedWordElement(0x0106), //
    +								new ElementToChannelConverter((value) -> {
    +									Integer intValue = TypeUtils.getAsType(OpenemsType.INTEGER, value);
    +									if (intValue != null) {
    +										switch (intValue) {
    +										case 1:
    +											return GridMode.OFF_GRID;
    +										case 2:
    +											return GridMode.ON_GRID;
    +										}
    +									}
    +									return GridMode.UNDEFINED;
    +								})),
    +						new DummyRegisterElement(0x0107), //
    +						m(EssFeneconCommercial40Impl.ChannelId.PROTOCOL_VERSION, new UnsignedWordElement(0x0108)),
    +						m(EssFeneconCommercial40Impl.ChannelId.SYSTEM_MANUFACTURER, new UnsignedWordElement(0x0109)),
    +						m(EssFeneconCommercial40Impl.ChannelId.SYSTEM_TYPE, new UnsignedWordElement(0x010A)),
    +						new DummyRegisterElement(0x010B, 0x010F), //
    +						m(new BitsWordElement(0x0110, this) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_0) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_1)),
    +						m(new BitsWordElement(0x0111, this) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_2) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_3)),
    +						new DummyRegisterElement(0x0112, 0x0124), //
    +						m(new BitsWordElement(0x0125, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_4) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_5) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_6) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_7) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_8) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_9)),
    +						m(new BitsWordElement(0x0126, this) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_10)), //
    +						new DummyRegisterElement(0x0127, 0x014F), //
    +						m(EssFeneconCommercial40Impl.ChannelId.BATTERY_STRING_SWITCH_STATE,
    +								new UnsignedWordElement(0x0150))), //
    +				new FC3ReadRegistersTask(0x0180, Priority.LOW, //
    +						m(new BitsWordElement(0x0180, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_11) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_12) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_13) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_14) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_15) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_16) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_17) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_18) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_19) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_20) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_21) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_22) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_23)),
    +						new DummyRegisterElement(0x0181), //
    +						m(new BitsWordElement(0x0182, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_24) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_25) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_26) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_27) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_28) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_29) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_30) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_31) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_32) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_33) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_34) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_35) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_36) //
    +								.bit(13, EssFeneconCommercial40Impl.ChannelId.STATE_37) //
    +								.bit(14, EssFeneconCommercial40Impl.ChannelId.STATE_38) //
    +								.bit(15, EssFeneconCommercial40Impl.ChannelId.STATE_39)),
    +						m(new BitsWordElement(0x0183, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_40) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_41) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_42) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_43) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_44) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_45) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_46) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_47) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_48) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_49) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_50) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_51) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_52) //
    +								.bit(13, EssFeneconCommercial40Impl.ChannelId.STATE_53) //
    +								.bit(14, EssFeneconCommercial40Impl.ChannelId.STATE_54) //
    +								.bit(15, EssFeneconCommercial40Impl.ChannelId.STATE_55)),
    +						m(new BitsWordElement(0x0184, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_56) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_57) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_58) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_59) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_60) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_61) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_62) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_63) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_64) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_65) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_66) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_67) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_68) //
    +								.bit(13, EssFeneconCommercial40Impl.ChannelId.STATE_69) //
    +								.bit(14, EssFeneconCommercial40Impl.ChannelId.STATE_70) //
    +								.bit(15, EssFeneconCommercial40Impl.ChannelId.STATE_71)),
    +						m(new BitsWordElement(0x0185, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_72) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_73) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_74) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_75) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_76) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_77) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_78) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_79) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_80) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_81) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_82) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_83) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_84) //
    +								.bit(13, EssFeneconCommercial40Impl.ChannelId.STATE_85)),
    +						m(new BitsWordElement(0x0186, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_86) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_87) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_88) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_89) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_90) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_91) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_92) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_93) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_94) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_95) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_96) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_97) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_98) //
    +								.bit(13, EssFeneconCommercial40Impl.ChannelId.STATE_99) //
    +								.bit(14, EssFeneconCommercial40Impl.ChannelId.STATE_100) //
    +								.bit(15, EssFeneconCommercial40Impl.ChannelId.STATE_101)),
    +						m(new BitsWordElement(0x0187, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_102) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_103) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_104) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_105) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_106) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_107) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_108) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_109) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_110) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_111) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_112) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_113) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_114) //
    +								.bit(13, EssFeneconCommercial40Impl.ChannelId.STATE_115) //
    +								.bit(14, EssFeneconCommercial40Impl.ChannelId.STATE_116)),
    +						m(new BitsWordElement(0x0188, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_117) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_118) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_119) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_120) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_121) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_122) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_123) //
    +								.bit(14, EssFeneconCommercial40Impl.ChannelId.STATE_124))),
    +				new FC3ReadRegistersTask(0x0200, Priority.HIGH, //
    +						m(EssFeneconCommercial40Impl.ChannelId.BATTERY_VOLTAGE, new SignedWordElement(0x0200),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.BATTERY_CURRENT, new SignedWordElement(0x0201),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.BATTERY_POWER, new SignedWordElement(0x0202),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						new DummyRegisterElement(0x0203, 0x0207),
    +						m(SymmetricEss.ChannelId.ACTIVE_CHARGE_ENERGY,
    +								new UnsignedDoublewordElement(0x0208).wordOrder(WordOrder.LSWMSW),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(SymmetricEss.ChannelId.ACTIVE_DISCHARGE_ENERGY,
    +								new UnsignedDoublewordElement(0x020A).wordOrder(WordOrder.LSWMSW),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						new DummyRegisterElement(0x020C, 0x020F), //
    +						m(EssFeneconCommercial40Impl.ChannelId.GRID_ACTIVE_POWER, new SignedWordElement(0x0210),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(SymmetricEss.ChannelId.REACTIVE_POWER, new SignedWordElement(0x0211),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.APPARENT_POWER, new UnsignedWordElement(0x0212),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.CURRENT_L1, new SignedWordElement(0x0213),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.CURRENT_L2, new SignedWordElement(0x0214),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.CURRENT_L3, new SignedWordElement(0x0215),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						new DummyRegisterElement(0x0216, 0x218), //
    +						m(EssFeneconCommercial40Impl.ChannelId.VOLTAGE_L1, new UnsignedWordElement(0x0219),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.VOLTAGE_L2, new UnsignedWordElement(0x021A),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.VOLTAGE_L3, new UnsignedWordElement(0x021B),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.FREQUENCY, new UnsignedWordElement(0x021C)), //
    +						new DummyRegisterElement(0x021D, 0x0221), //
    +						m(EssFeneconCommercial40Impl.ChannelId.INVERTER_VOLTAGE_L1, new UnsignedWordElement(0x0222),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.INVERTER_VOLTAGE_L2, new UnsignedWordElement(0x0223),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.INVERTER_VOLTAGE_L3, new UnsignedWordElement(0x0224),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.INVERTER_CURRENT_L1, new SignedWordElement(0x0225),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.INVERTER_CURRENT_L2, new SignedWordElement(0x0226),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.INVERTER_CURRENT_L3, new SignedWordElement(0x0227),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(SymmetricEss.ChannelId.ACTIVE_POWER, new SignedWordElement(0x0228),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						new DummyRegisterElement(0x0229, 0x022F), //
    +						m(ChannelId.ORIGINAL_ALLOWED_CHARGE_POWER, new SignedWordElement(0x0230),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(ChannelId.ORIGINAL_ALLOWED_DISCHARGE_POWER, new UnsignedWordElement(0x0231),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(SymmetricEss.ChannelId.MAX_APPARENT_POWER, new UnsignedWordElement(0x0232),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						new DummyRegisterElement(0x0233, 0x23F),
    +						m(EssFeneconCommercial40Impl.ChannelId.IPM_TEMPERATURE_L1, new SignedWordElement(0x0240)), //
    +						m(EssFeneconCommercial40Impl.ChannelId.IPM_TEMPERATURE_L2, new SignedWordElement(0x0241)), //
    +						m(EssFeneconCommercial40Impl.ChannelId.IPM_TEMPERATURE_L3, new SignedWordElement(0x0242)), //
    +						new DummyRegisterElement(0x0243, 0x0248), //
    +						m(EssFeneconCommercial40Impl.ChannelId.TRANSFORMER_TEMPERATURE_L2,
    +								new SignedWordElement(0x0249))), //
    +				new FC16WriteRegistersTask(0x0500, //
    +						m(EssFeneconCommercial40Impl.ChannelId.SET_WORK_STATE, new UnsignedWordElement(0x0500))), //
    +				new FC16WriteRegistersTask(0x0501, //
    +						m(EssFeneconCommercial40Impl.ChannelId.SET_ACTIVE_POWER, new SignedWordElement(0x0501),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.SET_REACTIVE_POWER, new SignedWordElement(0x0502),
    +								ElementToChannelConverter.SCALE_FACTOR_2), //
    +						m(EssFeneconCommercial40Impl.ChannelId.SET_PV_POWER_LIMIT, new UnsignedWordElement(0x0503),
    +								ElementToChannelConverter.SCALE_FACTOR_2)), //
    +				new FC3ReadRegistersTask(0xA000, Priority.LOW, //
    +						m(EssFeneconCommercial40Impl.ChannelId.BMS_DCDC_WORK_STATE, new UnsignedWordElement(0xA000)), //
    +						m(EssFeneconCommercial40Impl.ChannelId.BMS_DCDC_WORK_MODE, new UnsignedWordElement(0xA001))), //
    +				new FC3ReadRegistersTask(0xA100, Priority.LOW, //
    +						m(new BitsWordElement(0xA100, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_125) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_126) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_127) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_128) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_129) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_130)),
    +						m(new BitsWordElement(0xA101, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_131) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_132) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_133) //
    +								.bit(3, EssFeneconCommercial40Impl.ChannelId.STATE_134) //
    +								.bit(4, EssFeneconCommercial40Impl.ChannelId.STATE_135) //
    +								.bit(5, EssFeneconCommercial40Impl.ChannelId.STATE_136) //
    +								.bit(6, EssFeneconCommercial40Impl.ChannelId.STATE_137) //
    +								.bit(7, EssFeneconCommercial40Impl.ChannelId.STATE_138) //
    +								.bit(8, EssFeneconCommercial40Impl.ChannelId.STATE_139) //
    +								.bit(9, EssFeneconCommercial40Impl.ChannelId.STATE_140) //
    +								.bit(10, EssFeneconCommercial40Impl.ChannelId.STATE_141) //
    +								.bit(11, EssFeneconCommercial40Impl.ChannelId.STATE_142) //
    +								.bit(12, EssFeneconCommercial40Impl.ChannelId.STATE_143) //
    +								.bit(13, EssFeneconCommercial40Impl.ChannelId.STATE_144) //
    +								.bit(14, EssFeneconCommercial40Impl.ChannelId.STATE_145) //
    +								.bit(15, EssFeneconCommercial40Impl.ChannelId.STATE_146)),
    +						m(new BitsWordElement(0xA102, this) //
    +								.bit(0, EssFeneconCommercial40Impl.ChannelId.STATE_147) //
    +								.bit(1, EssFeneconCommercial40Impl.ChannelId.STATE_148) //
    +								.bit(2, EssFeneconCommercial40Impl.ChannelId.STATE_149))),
    +				new FC3ReadRegistersTask(0x1500, Priority.LOW,
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_1_VOLTAGE, new UnsignedWordElement(0x1500)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_2_VOLTAGE, new UnsignedWordElement(0x1501)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_3_VOLTAGE, new UnsignedWordElement(0x1502)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_4_VOLTAGE, new UnsignedWordElement(0x1503)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_5_VOLTAGE, new UnsignedWordElement(0x1504)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_6_VOLTAGE, new UnsignedWordElement(0x1505)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_7_VOLTAGE, new UnsignedWordElement(0x1506)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_8_VOLTAGE, new UnsignedWordElement(0x1507)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_9_VOLTAGE, new UnsignedWordElement(0x1508)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_10_VOLTAGE, new UnsignedWordElement(0x1509)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_11_VOLTAGE, new UnsignedWordElement(0x150A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_12_VOLTAGE, new UnsignedWordElement(0x150B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_13_VOLTAGE, new UnsignedWordElement(0x150C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_14_VOLTAGE, new UnsignedWordElement(0x150D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_15_VOLTAGE, new UnsignedWordElement(0x150E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_16_VOLTAGE, new UnsignedWordElement(0x150F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_17_VOLTAGE, new UnsignedWordElement(0x1510)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_18_VOLTAGE, new UnsignedWordElement(0x1511)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_19_VOLTAGE, new UnsignedWordElement(0x1512)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_20_VOLTAGE, new UnsignedWordElement(0x1513)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_21_VOLTAGE, new UnsignedWordElement(0x1514)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_22_VOLTAGE, new UnsignedWordElement(0x1515)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_23_VOLTAGE, new UnsignedWordElement(0x1516)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_24_VOLTAGE, new UnsignedWordElement(0x1517)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_25_VOLTAGE, new UnsignedWordElement(0x1518)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_26_VOLTAGE, new UnsignedWordElement(0x1519)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_27_VOLTAGE, new UnsignedWordElement(0x151A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_28_VOLTAGE, new UnsignedWordElement(0x151B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_29_VOLTAGE, new UnsignedWordElement(0x151C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_30_VOLTAGE, new UnsignedWordElement(0x151D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_31_VOLTAGE, new UnsignedWordElement(0x151E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_32_VOLTAGE, new UnsignedWordElement(0x151F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_33_VOLTAGE, new UnsignedWordElement(0x1520)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_34_VOLTAGE, new UnsignedWordElement(0x1521)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_35_VOLTAGE, new UnsignedWordElement(0x1522)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_36_VOLTAGE, new UnsignedWordElement(0x1523)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_37_VOLTAGE, new UnsignedWordElement(0x1524)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_38_VOLTAGE, new UnsignedWordElement(0x1525)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_39_VOLTAGE, new UnsignedWordElement(0x1526)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_40_VOLTAGE, new UnsignedWordElement(0x1527)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_41_VOLTAGE, new UnsignedWordElement(0x1528)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_42_VOLTAGE, new UnsignedWordElement(0x1529)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_43_VOLTAGE, new UnsignedWordElement(0x152A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_44_VOLTAGE, new UnsignedWordElement(0x152B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_45_VOLTAGE, new UnsignedWordElement(0x152C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_46_VOLTAGE, new UnsignedWordElement(0x152D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_47_VOLTAGE, new UnsignedWordElement(0x152E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_48_VOLTAGE, new UnsignedWordElement(0x152F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_49_VOLTAGE, new UnsignedWordElement(0x1530)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_50_VOLTAGE, new UnsignedWordElement(0x1531)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_51_VOLTAGE, new UnsignedWordElement(0x1532)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_52_VOLTAGE, new UnsignedWordElement(0x1533)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_53_VOLTAGE, new UnsignedWordElement(0x1534)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_54_VOLTAGE, new UnsignedWordElement(0x1535)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_55_VOLTAGE, new UnsignedWordElement(0x1536)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_56_VOLTAGE, new UnsignedWordElement(0x1537)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_57_VOLTAGE, new UnsignedWordElement(0x1538)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_58_VOLTAGE, new UnsignedWordElement(0x1539)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_59_VOLTAGE, new UnsignedWordElement(0x153A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_60_VOLTAGE, new UnsignedWordElement(0x153B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_61_VOLTAGE, new UnsignedWordElement(0x153C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_62_VOLTAGE, new UnsignedWordElement(0x153D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_63_VOLTAGE, new UnsignedWordElement(0x153E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_64_VOLTAGE, new UnsignedWordElement(0x153F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_65_VOLTAGE, new UnsignedWordElement(0x1540)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_66_VOLTAGE, new UnsignedWordElement(0x1541)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_67_VOLTAGE, new UnsignedWordElement(0x1542)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_68_VOLTAGE, new UnsignedWordElement(0x1543)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_69_VOLTAGE, new UnsignedWordElement(0x1544)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_70_VOLTAGE, new UnsignedWordElement(0x1545)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_71_VOLTAGE, new UnsignedWordElement(0x1546)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_72_VOLTAGE, new UnsignedWordElement(0x1547)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_73_VOLTAGE, new UnsignedWordElement(0x1548)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_74_VOLTAGE, new UnsignedWordElement(0x1549)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_75_VOLTAGE, new UnsignedWordElement(0x154A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_76_VOLTAGE, new UnsignedWordElement(0x154B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_77_VOLTAGE, new UnsignedWordElement(0x154C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_78_VOLTAGE, new UnsignedWordElement(0x154D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_79_VOLTAGE, new UnsignedWordElement(0x154E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_80_VOLTAGE, new UnsignedWordElement(0x154F))),
    +				new FC3ReadRegistersTask(0x1550, Priority.LOW,
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_81_VOLTAGE, new UnsignedWordElement(0x1550)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_82_VOLTAGE, new UnsignedWordElement(0x1551)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_83_VOLTAGE, new UnsignedWordElement(0x1552)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_84_VOLTAGE, new UnsignedWordElement(0x1553)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_85_VOLTAGE, new UnsignedWordElement(0x1554)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_86_VOLTAGE, new UnsignedWordElement(0x1555)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_87_VOLTAGE, new UnsignedWordElement(0x1556)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_88_VOLTAGE, new UnsignedWordElement(0x1557)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_89_VOLTAGE, new UnsignedWordElement(0x1558)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_90_VOLTAGE, new UnsignedWordElement(0x1559)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_91_VOLTAGE, new UnsignedWordElement(0x155A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_92_VOLTAGE, new UnsignedWordElement(0x155B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_93_VOLTAGE, new UnsignedWordElement(0x155C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_94_VOLTAGE, new UnsignedWordElement(0x155D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_95_VOLTAGE, new UnsignedWordElement(0x155E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_96_VOLTAGE, new UnsignedWordElement(0x155F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_97_VOLTAGE, new UnsignedWordElement(0x1560)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_98_VOLTAGE, new UnsignedWordElement(0x1561)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_99_VOLTAGE, new UnsignedWordElement(0x1562)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_100_VOLTAGE, new UnsignedWordElement(0x1563)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_101_VOLTAGE, new UnsignedWordElement(0x1564)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_102_VOLTAGE, new UnsignedWordElement(0x1565)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_103_VOLTAGE, new UnsignedWordElement(0x1566)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_104_VOLTAGE, new UnsignedWordElement(0x1567)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_105_VOLTAGE, new UnsignedWordElement(0x1568)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_106_VOLTAGE, new UnsignedWordElement(0x1569)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_107_VOLTAGE, new UnsignedWordElement(0x156A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_108_VOLTAGE, new UnsignedWordElement(0x156B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_109_VOLTAGE, new UnsignedWordElement(0x156C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_110_VOLTAGE, new UnsignedWordElement(0x156D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_111_VOLTAGE, new UnsignedWordElement(0x156E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_112_VOLTAGE, new UnsignedWordElement(0x156F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_113_VOLTAGE, new UnsignedWordElement(0x1570)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_114_VOLTAGE, new UnsignedWordElement(0x1571)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_115_VOLTAGE, new UnsignedWordElement(0x1572)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_116_VOLTAGE, new UnsignedWordElement(0x1573)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_117_VOLTAGE, new UnsignedWordElement(0x1574)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_118_VOLTAGE, new UnsignedWordElement(0x1575)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_119_VOLTAGE, new UnsignedWordElement(0x1576)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_120_VOLTAGE, new UnsignedWordElement(0x1577)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_121_VOLTAGE, new UnsignedWordElement(0x1578)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_122_VOLTAGE, new UnsignedWordElement(0x1579)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_123_VOLTAGE, new UnsignedWordElement(0x157A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_124_VOLTAGE, new UnsignedWordElement(0x157B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_125_VOLTAGE, new UnsignedWordElement(0x157C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_126_VOLTAGE, new UnsignedWordElement(0x157D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_127_VOLTAGE, new UnsignedWordElement(0x157E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_128_VOLTAGE, new UnsignedWordElement(0x157F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_129_VOLTAGE, new UnsignedWordElement(0x1580)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_130_VOLTAGE, new UnsignedWordElement(0x1581)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_131_VOLTAGE, new UnsignedWordElement(0x1582)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_132_VOLTAGE, new UnsignedWordElement(0x1583)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_133_VOLTAGE, new UnsignedWordElement(0x1584)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_134_VOLTAGE, new UnsignedWordElement(0x1585)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_135_VOLTAGE, new UnsignedWordElement(0x1586)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_136_VOLTAGE, new UnsignedWordElement(0x1587)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_137_VOLTAGE, new UnsignedWordElement(0x1588)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_138_VOLTAGE, new UnsignedWordElement(0x1589)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_139_VOLTAGE, new UnsignedWordElement(0x158A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_140_VOLTAGE, new UnsignedWordElement(0x158B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_141_VOLTAGE, new UnsignedWordElement(0x158C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_142_VOLTAGE, new UnsignedWordElement(0x158D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_143_VOLTAGE, new UnsignedWordElement(0x158E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_144_VOLTAGE, new UnsignedWordElement(0x158F)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_145_VOLTAGE, new UnsignedWordElement(0x1590)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_146_VOLTAGE, new UnsignedWordElement(0x1591)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_147_VOLTAGE, new UnsignedWordElement(0x1592)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_148_VOLTAGE, new UnsignedWordElement(0x1593)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_149_VOLTAGE, new UnsignedWordElement(0x1594)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_150_VOLTAGE, new UnsignedWordElement(0x1595)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_151_VOLTAGE, new UnsignedWordElement(0x1596)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_152_VOLTAGE, new UnsignedWordElement(0x1597)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_153_VOLTAGE, new UnsignedWordElement(0x1598)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_154_VOLTAGE, new UnsignedWordElement(0x1599)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_155_VOLTAGE, new UnsignedWordElement(0x159A)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_156_VOLTAGE, new UnsignedWordElement(0x159B)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_157_VOLTAGE, new UnsignedWordElement(0x159C)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_158_VOLTAGE, new UnsignedWordElement(0x159D)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_159_VOLTAGE, new UnsignedWordElement(0x159E)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_160_VOLTAGE, new UnsignedWordElement(0x159F))),
    +				new FC3ReadRegistersTask(0x15A0, Priority.LOW,
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_161_VOLTAGE, new UnsignedWordElement(0x15A0)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_162_VOLTAGE, new UnsignedWordElement(0x15A1)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_163_VOLTAGE, new UnsignedWordElement(0x15A2)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_164_VOLTAGE, new UnsignedWordElement(0x15A3)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_165_VOLTAGE, new UnsignedWordElement(0x15A4)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_166_VOLTAGE, new UnsignedWordElement(0x15A5)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_167_VOLTAGE, new UnsignedWordElement(0x15A6)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_168_VOLTAGE, new UnsignedWordElement(0x15A7)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_169_VOLTAGE, new UnsignedWordElement(0x15A8)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_170_VOLTAGE, new UnsignedWordElement(0x15A9)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_171_VOLTAGE, new UnsignedWordElement(0x15AA)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_172_VOLTAGE, new UnsignedWordElement(0x15AB)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_173_VOLTAGE, new UnsignedWordElement(0x15AC)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_174_VOLTAGE, new UnsignedWordElement(0x15AD)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_175_VOLTAGE, new UnsignedWordElement(0x15AE)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_176_VOLTAGE, new UnsignedWordElement(0x15AF)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_177_VOLTAGE, new UnsignedWordElement(0x15B0)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_178_VOLTAGE, new UnsignedWordElement(0x15B1)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_179_VOLTAGE, new UnsignedWordElement(0x15B2)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_180_VOLTAGE, new UnsignedWordElement(0x15B3)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_181_VOLTAGE, new UnsignedWordElement(0x15B4)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_182_VOLTAGE, new UnsignedWordElement(0x15B5)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_183_VOLTAGE, new UnsignedWordElement(0x15B6)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_184_VOLTAGE, new UnsignedWordElement(0x15B7)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_185_VOLTAGE, new UnsignedWordElement(0x15B8)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_186_VOLTAGE, new UnsignedWordElement(0x15B9)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_187_VOLTAGE, new UnsignedWordElement(0x15BA)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_188_VOLTAGE, new UnsignedWordElement(0x15BB)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_189_VOLTAGE, new UnsignedWordElement(0x15BC)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_190_VOLTAGE, new UnsignedWordElement(0x15BD)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_191_VOLTAGE, new UnsignedWordElement(0x15BE)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_192_VOLTAGE, new UnsignedWordElement(0x15BF)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_193_VOLTAGE, new UnsignedWordElement(0x15C0)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_194_VOLTAGE, new UnsignedWordElement(0x15C1)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_195_VOLTAGE, new UnsignedWordElement(0x15C2)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_196_VOLTAGE, new UnsignedWordElement(0x15C3)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_197_VOLTAGE, new UnsignedWordElement(0x15C4)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_198_VOLTAGE, new UnsignedWordElement(0x15C5)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_199_VOLTAGE, new UnsignedWordElement(0x15C6)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_200_VOLTAGE, new UnsignedWordElement(0x15C7)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_201_VOLTAGE, new UnsignedWordElement(0x15C8)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_202_VOLTAGE, new UnsignedWordElement(0x15C9)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_203_VOLTAGE, new UnsignedWordElement(0x15CA)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_204_VOLTAGE, new UnsignedWordElement(0x15CB)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_205_VOLTAGE, new UnsignedWordElement(0x15CC)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_206_VOLTAGE, new UnsignedWordElement(0x15CD)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_207_VOLTAGE, new UnsignedWordElement(0x15CE)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_208_VOLTAGE, new UnsignedWordElement(0x15CF)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_209_VOLTAGE, new UnsignedWordElement(0x15D0)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_210_VOLTAGE, new UnsignedWordElement(0x15D1)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_211_VOLTAGE, new UnsignedWordElement(0x15D2)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_212_VOLTAGE, new UnsignedWordElement(0x15D3)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_213_VOLTAGE, new UnsignedWordElement(0x15D4)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_214_VOLTAGE, new UnsignedWordElement(0x15D5)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_215_VOLTAGE, new UnsignedWordElement(0x15D6)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_216_VOLTAGE, new UnsignedWordElement(0x15D7)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_217_VOLTAGE, new UnsignedWordElement(0x15D8)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_218_VOLTAGE, new UnsignedWordElement(0x15D9)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_219_VOLTAGE, new UnsignedWordElement(0x15DA)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_220_VOLTAGE, new UnsignedWordElement(0x15DB)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_221_VOLTAGE, new UnsignedWordElement(0x15DC)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_222_VOLTAGE, new UnsignedWordElement(0x15DD)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_223_VOLTAGE, new UnsignedWordElement(0x15DE)),
    +						m(EssFeneconCommercial40Impl.ChannelId.CELL_224_VOLTAGE, new UnsignedWordElement(0x15DF))),
    +				new FC3ReadRegistersTask(0x1402, Priority.LOW, //
    +						m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(0x1402))));
    +	}
    +
    +	@Override
    +	public String debugLog() {
    +		return "SoC:" + this.getSoc().value().asString() //
    +				+ "|L:" + this.getActivePower().value().asString() //
    +				+ "|Allowed:"
    +				+ this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER).value().asStringWithoutUnit() + ";"
    +				+ this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER).value().asString() //
    +				+ "|" + this.getGridMode().value().asOptionString();
    +	}
    +
    +	@Override
    +	public void handleEvent(Event event) {
    +		if (!this.isEnabled()) {
    +			return;
    +		}
    +		switch (event.getTopic()) {
    +		case EdgeEventConstants.TOPIC_CYCLE_BEFORE_CONTROLLERS:
    +			this.defineWorkState();
    +			break;
    +		}
    +	}
    +
    +	private LocalDateTime lastDefineWorkState = null;
    +
    +	private void defineWorkState() {
    +		/*
    +		 * Set ESS in running mode
    +		 */
    +		// TODO this should be smarter: set in energy saving mode if there was no output
    +		// power for a while and we don't need emergency power.
    +		LocalDateTime now = LocalDateTime.now();
    +		if (lastDefineWorkState == null || now.minusMinutes(1).isAfter(this.lastDefineWorkState)) {
    +			this.lastDefineWorkState = now;
    +			EnumWriteChannel setWorkStateChannel = this.channel(ChannelId.SET_WORK_STATE);
    +			try {
    +				setWorkStateChannel.setNextWriteValue(SetWorkState.START);
    +			} catch (OpenemsNamedException e) {
    +				logError(this.log, "Unable to start: " + e.getMessage());
    +			}
    +		}
    +	}
    +
    +	@Override
    +	public Power getPower() {
    +		return this.power;
    +	}
    +
    +	@Override
    +	public int getPowerPrecision() {
    +		return 100; // the modbus field for SetActivePower has the unit 0.1 kW
    +	}
    +
    +	@Override
    +	public Constraint[] getStaticConstraints() {
    +		if (this.readOnlyMode) {
    +			return new Constraint[] { //
    +					this.createPowerConstraint("Read-Only-Mode", Phase.ALL, Pwr.ACTIVE, Relationship.EQUALS, 0), //
    +					this.createPowerConstraint("Read-Only-Mode", Phase.ALL, Pwr.REACTIVE, Relationship.EQUALS, 0) //
    +			};
    +		} else {
    +			return new Constraint[] {
    +					// ReactivePower limitations
    +					this.createPowerConstraint("Commercial40 Min Reactive Power", Phase.ALL, Pwr.REACTIVE,
    +							Relationship.GREATER_OR_EQUALS, MIN_REACTIVE_POWER),
    +					this.createPowerConstraint("Commercial40 Max Reactive Power", Phase.ALL, Pwr.REACTIVE,
    +							Relationship.LESS_OR_EQUALS, MAX_REACTIVE_POWER) };
    +		}
    +	}
    +
    +	@Override
    +	public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
    +		return new ModbusSlaveTable( //
    +				OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
    +				SymmetricEss.getModbusSlaveNatureTable(accessMode), //
    +				ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode) //
    +		);
    +	}
    +
    +}
    \ No newline at end of file
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/InverterState.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/InverterState.java
    index fa1da3f55dd..19cb7567a2e 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/InverterState.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/InverterState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum InverterState implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SetWorkState.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SetWorkState.java
    index 30fb5485e29..61c5be9bd8c 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SetWorkState.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SetWorkState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum SetWorkState implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemManufacturer.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemManufacturer.java
    index 964ed323df8..9ea946d8f03 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemManufacturer.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemManufacturer.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum SystemManufacturer implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemState.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemState.java
    index 97b5ae2b695..d5bd91223b3 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemState.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum SystemState implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemType.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemType.java
    index 946e4598eb5..005e8cdae99 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemType.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/SystemType.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.fenecon.commercial40;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum SystemType implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/Config.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/Config.java
    index 0fc2c7a3ef6..25028ae0077 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/Config.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/Config.java
    @@ -7,8 +7,14 @@
     		name = "ESS FENECON Commercial 40 DC Charger", //
     		description = "Implements the FENECON Commercial 40 DC Charger.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "charger0";
     
    +	@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 = "FENECON Commercial40-ID", description = "ID of FENECON Commercial 40 device.")
    diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/EssDcChargerFeneconCommercial40.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/EssDcChargerFeneconCommercial40.java
    index 215169be9c8..d84c33ff140 100644
    --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/EssDcChargerFeneconCommercial40.java
    +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/EssDcChargerFeneconCommercial40.java
    @@ -1,6 +1,5 @@
     package io.openems.edge.ess.fenecon.commercial40.charger;
     
    -import java.util.concurrent.atomic.AtomicReference;
     import java.util.function.Consumer;
     
     import org.osgi.service.cm.ConfigurationAdmin;
    @@ -15,6 +14,7 @@
     import org.osgi.service.component.annotations.ReferencePolicyOption;
     import org.osgi.service.metatype.annotations.Designate;
     
    +import io.openems.common.channel.Unit;
     import io.openems.common.types.OpenemsType;
     import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
     import io.openems.edge.bridge.modbus.api.BridgeModbus;
    @@ -27,12 +27,10 @@
     import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
     import io.openems.edge.common.channel.Channel;
     import io.openems.edge.common.channel.Doc;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.channel.value.Value;
     import io.openems.edge.common.component.OpenemsComponent;
     import io.openems.edge.common.taskmanager.Priority;
     import io.openems.edge.common.type.TypeUtils;
    -import io.openems.edge.ess.api.ManagedSymmetricEss;
     import io.openems.edge.ess.dccharger.api.EssDcCharger;
     import io.openems.edge.ess.fenecon.commercial40.EssFeneconCommercial40;
     
    @@ -47,8 +45,6 @@ public class EssDcChargerFeneconCommercial40 extends AbstractOpenemsModbusCompon
     	// private final Logger log =
     	// LoggerFactory.getLogger(EssDcChargerFeneconCommercial40.class);
     
    -	private AtomicReference ess = new AtomicReference(null);
    -
     	@Reference
     	protected ConfigurationAdmin cm;
     
    @@ -89,16 +85,17 @@ protected void setModbus(BridgeModbus modbus) {
     	}
     
     	@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
    -	protected void setEss(ManagedSymmetricEss ess) {
    -		if (ess instanceof EssFeneconCommercial40) {
    -			this.ess.set((EssFeneconCommercial40) ess);
    -		}
    -	}
    +	private EssFeneconCommercial40 ess;
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled(), this.ess.get().getUnitId(), this.cm, "Modbus",
    -				this.ess.get().getModbusBridgeId());
    +		super.activate(context, config.id(), config.alias(), config.enabled(), this.ess.getUnitId(), this.cm, "Modbus",
    +				this.ess.getModbusBridgeId());
    +
    +		// update filter for 'Ess'
    +		if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "ess", config.ess_id())) {
    +			return;
    +		}
     	}
     
     	@Deactivate
    diff --git a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/Config.java b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/Config.java
    index 7bacc03eeb8..739f220ff7c 100644
    --- a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/Config.java
    +++ b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/Config.java
    @@ -5,10 +5,16 @@
     
     @ObjectClassDefinition( //
     		name = "ESS KACO blueplanet gridsave 50.0 TL3", //
    -		description = "Implements the FENECON Commercial 40 energy storage system.")
    +		description = "Implements the KACO blueplanet gridsave 50.0 TL3 inverter.")
     @interface Config {
    +
    +	@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
     	String id() default "ess0";
     
    +	@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 = "Modbus-ID", description = "ID of Modbus brige.")
    @@ -21,7 +27,7 @@
     	int watchdoginterval() default 0;
     
     	@AttributeDefinition(name = "Battery-ID", description = "ID of Battery.")
    -	String battery_id();
    +	String battery_id() default "bms0";
     
     	@AttributeDefinition(name = "Battery target filter", description = "This is auto-generated by 'Battery-ID'.")
     	String Battery_target() default "";
    diff --git a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/CurrentState.java b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/CurrentState.java
    index feb8685ff34..d32fbce2510 100644
    --- a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/CurrentState.java
    +++ b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/CurrentState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.kaco.blueplanet.gridsave50;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum CurrentState implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/ErrorCode.java b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/ErrorCode.java
    index cf98d40e880..5758a5a0923 100644
    --- a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/ErrorCode.java
    +++ b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/ErrorCode.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.kaco.blueplanet.gridsave50;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum ErrorCode implements OptionsEnum {
     	UNDEFINED(-1, "Undefined"), //
    diff --git a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/EssKacoBlueplanetGridsave50.java b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/EssKacoBlueplanetGridsave50.java
    index 80d2b598916..332037a5824 100644
    --- a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/EssKacoBlueplanetGridsave50.java
    +++ b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/EssKacoBlueplanetGridsave50.java
    @@ -21,6 +21,9 @@
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     
    +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.channel.AccessMode;
    +import io.openems.common.channel.Unit;
     import io.openems.common.exceptions.OpenemsException;
     import io.openems.common.types.OpenemsType;
     import io.openems.edge.battery.api.Battery;
    @@ -34,18 +37,17 @@
     import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement;
     import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask;
     import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
    -import io.openems.edge.common.channel.AccessMode;
     import io.openems.edge.common.channel.Doc;
     import io.openems.edge.common.channel.EnumReadChannel;
     import io.openems.edge.common.channel.EnumWriteChannel;
     import io.openems.edge.common.channel.IntegerDoc;
     import io.openems.edge.common.channel.IntegerReadChannel;
     import io.openems.edge.common.channel.IntegerWriteChannel;
    -import io.openems.edge.common.channel.Unit;
     import io.openems.edge.common.component.OpenemsComponent;
     import io.openems.edge.common.event.EdgeEventConstants;
     import io.openems.edge.common.modbusslave.ModbusSlave;
     import io.openems.edge.common.modbusslave.ModbusSlaveTable;
    +import io.openems.edge.common.sum.GridMode;
     import io.openems.edge.common.taskmanager.Priority;
     import io.openems.edge.common.type.TypeUtils;
     import io.openems.edge.ess.api.ManagedSymmetricEss;
    @@ -102,6 +104,7 @@ public EssKacoBlueplanetGridsave50() {
     				ManagedSymmetricEss.ChannelId.values(), //
     				ChannelId.values() //
     		);
    +		this.channel(SymmetricEss.ChannelId.GRID_MODE).setNextValue(GridMode.ON_GRID);
     	}
     
     	@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
    @@ -111,7 +114,8 @@ protected void setModbus(BridgeModbus modbus) {
     
     	@Activate
     	void activate(ComponentContext context, Config config) {
    -		super.activate(context, config.id(), config.enabled(), DEFAULT_UNIT_ID, this.cm, "Modbus", config.modbus_id()); //
    +		super.activate(context, config.id(), config.alias(), config.enabled(), DEFAULT_UNIT_ID, this.cm, "Modbus",
    +				config.modbus_id()); //
     		// update filter for 'battery'
     		if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "battery", config.battery_id())) {
     			return;
    @@ -197,7 +201,7 @@ public void applyPower(int activePower, int reactivePower) throws OpenemsExcepti
     
     			try {
     				wSetPctChannel.setNextWriteValue(WSetPct);
    -			} catch (OpenemsException e) {
    +			} catch (OpenemsNamedException e) {
     				log.error("EssKacoBlueplanetGridsave50.applyPower(): Problem occurred while trying so set active power"
     						+ e.getMessage());
     			}
    @@ -312,7 +316,7 @@ private void setBatteryRanges() {
     			this.getBatterySocChannel().setNextWriteValue(batSoC);
     			this.getBatterySohChannel().setNextWriteValue(batSoH);
     			this.getBatteryTempChannel().setNextWriteValue(batTemp);
    -		} catch (OpenemsException e) {
    +		} catch (OpenemsNamedException e) {
     			log.error("Error during setBatteryRanges, " + e.getMessage());
     		}
     	}
    @@ -400,7 +404,7 @@ private void startGridMode() {
     		EnumWriteChannel requestedState = this.channel(ChannelId.REQUESTED_STATE);
     		try {
     			requestedState.setNextWriteValue(RequestedState.GRID_CONNECTED.getValue());
    -		} catch (OpenemsException e) {
    +		} catch (OpenemsNamedException e) {
     			log.error("problem occurred while trying to start grid mode" + e.getMessage());
     		}
     	}
    @@ -409,7 +413,7 @@ private void startSystem() {
     		EnumWriteChannel requestedState = this.channel(ChannelId.REQUESTED_STATE);
     		try {
     			requestedState.setNextWriteValue(RequestedState.STANDBY.getValue());
    -		} catch (OpenemsException e) {
    +		} catch (OpenemsNamedException e) {
     			log.error("problem occurred while trying to start inverter" + e.getMessage());
     		}
     	}
    @@ -418,7 +422,7 @@ private void stopSystem() {
     		EnumWriteChannel requestedState = this.channel(ChannelId.REQUESTED_STATE);
     		try {
     			requestedState.setNextWriteValue(RequestedState.OFF.getValue());
    -		} catch (OpenemsException e) {
    +		} catch (OpenemsNamedException e) {
     			log.error("problem occurred while trying to stop system" + e.getMessage());
     		}
     	}
    @@ -428,7 +432,7 @@ private void setWatchdog() {
     		IntegerWriteChannel watchdogChannel = this.channel(ChannelId.WATCHDOG);
     		try {
     			watchdogChannel.setNextWriteValue(watchdogInterval);
    -		} catch (OpenemsException e) {
    +		} catch (OpenemsNamedException e) {
     			log.error("Watchdog timer could not be written!" + e.getMessage());
     		}
     	}
    @@ -490,6 +494,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
     		 * SUNSPEC_64201
     		 */
     		REQUESTED_STATE(Doc.of(RequestedState.values()) //
    +				.accessMode(AccessMode.WRITE_ONLY) //
     				.onInit(new EnumWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_REQUESTED_STATE))),
     		CURRENT_STATE(Doc.of(CurrentState.values())), //
     		WATCHDOG(Doc.of(OpenemsType.INTEGER).unit(Unit.SECONDS) //
    @@ -708,11 +713,11 @@ private IntegerWriteChannel getBatteryTempChannel() {
     	}
     
     	@Override
    -	public ModbusSlaveTable getModbusSlaveTable() {
    +	public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
     		return new ModbusSlaveTable( //
    -				OpenemsComponent.getModbusSlaveNatureTable(), //
    -				SymmetricEss.getModbusSlaveNatureTable(), //
    -				ManagedSymmetricEss.getModbusSlaveNatureTable() //
    +				OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
    +				SymmetricEss.getModbusSlaveNatureTable(accessMode), //
    +				ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode) //
     		);
     	}
     
    diff --git a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/RequestedState.java b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/RequestedState.java
    index 5c2eb9135d6..5044ffc89d6 100644
    --- a/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/RequestedState.java
    +++ b/io.openems.edge.ess.kaco.blueplanet.gridsave50/src/io/openems/edge/ess/kaco/blueplanet/gridsave50/RequestedState.java
    @@ -1,6 +1,6 @@
     package io.openems.edge.ess.kaco.blueplanet.gridsave50;
     
    -import io.openems.edge.common.channel.OptionsEnum;
    +import io.openems.common.types.OptionsEnum;
     
     public enum RequestedState implements OptionsEnum {
     	// directly addressable states
    diff --git a/io.openems.edge.ess.mr.gridcon/bnd.bnd b/io.openems.edge.ess.mr.gridcon/bnd.bnd
    index 39abe20aabf..d93f591c810 100644
    --- a/io.openems.edge.ess.mr.gridcon/bnd.bnd
    +++ b/io.openems.edge.ess.mr.gridcon/bnd.bnd
    @@ -7,7 +7,8 @@ Export-Package: \
     	io.openems.edge.ess.power.api
     Private-Package: \
     	io.openems.edge.ess.mr.gridcon,\
    -	io.openems.edge.ess.mr.gridcon.enums
    +	io.openems.edge.ess.mr.gridcon.enums,\
    +	io.openems.edge.ess.mr.gridcon.writeutils
     
     -includeresource: {readme.md}
     
    diff --git a/io.openems.edge.ess.mr.gridcon/doc/Fehlerliste.xlsx b/io.openems.edge.ess.mr.gridcon/doc/Fehlerliste.xlsx
    index b89194ce947..d4e9c3d72c0 100644
    Binary files a/io.openems.edge.ess.mr.gridcon/doc/Fehlerliste.xlsx and b/io.openems.edge.ess.mr.gridcon/doc/Fehlerliste.xlsx differ
    diff --git a/io.openems.edge.ess.mr.gridcon/readme.md b/io.openems.edge.ess.mr.gridcon/readme.md
    index 8e9cd90a7c1..bd737f6708e 100644
    --- a/io.openems.edge.ess.mr.gridcon/readme.md
    +++ b/io.openems.edge.ess.mr.gridcon/readme.md
    @@ -1,8 +1,55 @@
    -# io.openems.edge.ess.mr.gridcon
    +# MR Gridcon
     
    -${Bundle-Description}
    +## Main state machine
     
    -## Example
    +```
    +          +-----------------------------------------------------------------------------+
    +          v                                                                             |
    ++---------+---------+       +-------------+      +--------------------+         +-------+------+
    +|                   |       |             |      |                    |         |              |
    +|   Going On+Grid   +------>+   On+Grid   +----->+   Going Off+Grid   +-------->+   Off+Grid   |
    +|                   |       |             |      |                    |         |              |
    ++-------------------+       +------+------+      +--------------------+         +-------+------+
    +                                   ^                                                    ^
    +                                   +------------------+         +-----------------------+
    +from all                                              |         |
    ++-----------+                                      +--+---------+--+
    +|           |                                      |               |
    +|   Error   |                                      |   Undefined   |
    +|           |                                      |               |
    ++-----------+                                      +---------------+
    +```
     
    -## References
    +## Error state machine
     
    +// TODO
    +
    +## Going On-Grid state machine
    +
    +// TODO
    +
    +## On-Grid state machine
    +
    +```
    +      +-------------------+
    +      |                   |
    ++-----+----+         +----+----+
    +|          |         |         |
    +|   Idle   +---------+   Run   |
    +|          |         |         |
    ++-----+----+         +----+----+
    +      ^                   ^
    +      +----+         +----+
    +           |         |
    +        +--+---------+--+
    +        |               |
    +        |   Undefined   |
    +        |               |
    +        +---------------+
    +```
    +
    +## Going Off-Grid state machine
    +
    +// TODO
    +
    +## Off-Grid state machine
    \ No newline at end of file
    diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/Config.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/Config.java
    index 4bd516ba79d..0560c1af089 100644
    --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/Config.java
    +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/Config.java
    @@ -8,9 +8,14 @@
     @ObjectClassDefinition( //
     		name = "ESS MR Gridcon PCS", //
     		description = "Implements the FENECON MR Gridcon PCS system")
    +public
     @interface Config {
     	String id() default "ess0";
     
    +	@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 = "Modbus-ID", description = "ID of Modbus brige.")
    @@ -32,13 +37,30 @@
     	InverterCount inverterCount() default InverterCount.ONE;
     
     	@AttributeDefinition(name = "MinSoCA", description = "Minimal SoC of Battery String A, if reached no further discharging is allowed")
    -	int minSocA() default 25;
    +	int minSocBatteryA() default 25;
     
     	@AttributeDefinition(name = "MinSoCB", description = "Minimal SoC of Battery String B, if reached no further discharging is allowed")
    -	int minSocB() default 25;
    +	int minSocBatteryB() default 25;
     
    +	// TODO Component was not able to start because key 'minSocC' exists already...
    +	// renaming to 'minSocBatteryC' works
     	@AttributeDefinition(name = "MinSoCC", description = "Minimal SoC of Battery String C, if reached no further discharging is allowed")
    -	int minSocC() default 25;
    +	int minSocBatteryC() default 25;
    +
    +	@AttributeDefinition(name = "WeightFactorBatteryA", description = "Weight factor for battery on string A, if more than one battery is connected, it is necessary to set the correct weighting for the battery strings to avoid battery damages. The total voltage of this battery is divided by this factor and compared to the others. An appropriate factor is the number of modules in the battery rack.")
    +	double weightFactorBatteryA() default 20;
    +
    +	@AttributeDefinition(name = "WeightFactorBatteryB", description = "Weight factor for battery on string B, if more than one battery is connected, it is necessary to set the correct weighting for the battery strings to avoid battery damages. The total voltage of this battery is divided by this factor and compared to the others. An appropriate factor is the number of modules in the battery rack.")
    +	double weightFactorBatteryB() default 20;
    +
    +	@AttributeDefinition(name = "WeightFactorBatteryC", description = "Weight factor for battery on string C, if more than one battery is connected, it is necessary to set the correct weighting for the battery strings to avoid battery damages. The total voltage of this battery is divided by this factor and compared to the others. An appropriate factor is the number of modules in the battery rack.")
    +	double weightFactorBatteryC() default 20;
    +
    +	@AttributeDefinition(name = "OverFrequency", description = "Frequency in millihertz that is added to grid frequency when going on grid")
    +	int overFrequency() default 200;
    +
    +	@AttributeDefinition(name = "OverVoltage", description = "Voltage in millivolt that is added to grid voltage when going on grid")
    +	int overVoltage() default 2000;
     
     	@AttributeDefinition(name = "Grid-Meter-ID", description = "ID of Grid-Meter")
     	String meter() default "meter0";
    @@ -62,4 +84,4 @@
     	String Modbus_target() default "";
     
     	String webconsole_configurationFactory_nameHint() default "ESS MR Gridcon PCS [{id}]";
    -}
    \ No newline at end of file
    +}
    diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/ErrorHandler.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/ErrorHandler.java
    new file mode 100644
    index 00000000000..ae7059d5364
    --- /dev/null
    +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/ErrorHandler.java
    @@ -0,0 +1,430 @@
    +package io.openems.edge.ess.mr.gridcon;
    +
    +import java.time.LocalDateTime;
    +import java.util.HashMap;
    +import java.util.Map;
    +import java.util.Optional;
    +
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +
    +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
    +import io.openems.common.types.OptionsEnum;
    +import io.openems.edge.common.channel.ChannelId;
    +import io.openems.edge.common.channel.IntegerReadChannel;
    +import io.openems.edge.common.channel.StateChannel;
    +import io.openems.edge.ess.mr.gridcon.enums.CCUState;
    +import io.openems.edge.ess.mr.gridcon.enums.ErrorCodeChannelId0;
    +import io.openems.edge.ess.mr.gridcon.enums.ErrorCodeChannelId1;
    +import io.openems.edge.ess.mr.gridcon.enums.ErrorDoc;
    +import io.openems.edge.ess.mr.gridcon.enums.GridConChannelId;
    +import io.openems.edge.ess.mr.gridcon.writeutils.CommandControlRegisters;
    +
    +public class ErrorHandler {
    +
    +	private final Logger log = LoggerFactory.getLogger(ErrorHandler.class);
    +	private final StateMachine parent;
    +	private final Map errorChannelIds;
    +
    +	private State state = State.UNDEFINED;
    +
    +	// READ_ERRORS
    +	private static final int DELAY_TO_WAIT_FOR_NEW_ERROR_SECONDS = 30;
    +	private Map readErrorMap = new HashMap<>();
    +
    +	// HANDLE_ERRORS
    +	private static final int MAX_TIMES_FOR_TRYING_TO_ACKNOWLEDGE_ERRORS = 5;
    +	private int tryToAcknowledgeErrorsCounter = 0;
    +
    +	protected LocalDateTime timeWhenErrorsHasBeenAcknowledged = null;
    +
    +	// HARD_RESET
    +	private LocalDateTime lastHardReset = null;
    +	private static final int SWITCH_OFF_TIME_SECONDS = 10;
    +	private static final long RELOAD_TIME_SECONDS = 60; // Time for Mr Gridcon to boot and come back
    +	private static final int MAX_TIMES_FOR_TRYING_TO_HARD_RESET = 5;
    +	private static final long DELAY_AFTER_FINISHING_SECONDS = 5;
    +	private int hardResetCounter = 0;
    +	private boolean sendSecondAcknowledge = false;
    +	private LocalDateTime delayAfterFinishing;
    +
    +	public ErrorHandler(StateMachine parent) {
    +		this.parent = parent;
    +		this.errorChannelIds = this.fillErrorChannelMap();
    +	}
    +
    +	public void initialize() {
    +		this.state = State.UNDEFINED;
    +		this.tryToAcknowledgeErrorsCounter = 0;
    +		this.readErrorMap = null;
    +		this.delayAfterFinishing = null;
    +	}
    +
    +	protected StateMachine.State run() throws IllegalArgumentException, OpenemsNamedException {
    +		switch (this.state) {
    +		case UNDEFINED:
    +			if (parent.getCcuState() == CCUState.ERROR) {
    +				this.state = State.READ_ERRORS;
    +			} else if (this.parent.isLinkVoltageTooLow()) {
    +				this.state = State.LINK_VOLTAGE_TOO_LOW;
    +			} else {
    +				this.state = State.READ_ERRORS;
    +			}
    +			break;
    +
    +		case LINK_VOLTAGE_TOO_LOW:
    +			this.state = this.doLinkVoltageTooLow();
    +			break;
    +
    +		case READ_ERRORS:
    +			this.state = this.doReadErrors();
    +			break;
    +
    +		case HANDLE_ERRORS:
    +			this.state = this.doHandleErrors();
    +			break;
    +
    +		case ACKNOWLEDGE_ERRORS:
    +			this.state = this.doAcknowledgeErrors();
    +			break;
    +
    +		case HARD_RESET:
    +			this.state = this.doHardReset();
    +			break;
    +
    +		case ERROR_HANDLING_NOT_POSSIBLE:
    +			this.state = this.doErrorHandlingNotPossible();
    +			break;
    +
    +		case FINISH_ERROR_HANDLING:
    +			//
    +			if (this.delayAfterFinishing == null) {
    +				this.delayAfterFinishing = LocalDateTime.now();
    +			}
    +
    +			if (this.delayAfterFinishing.plusSeconds(DELAY_AFTER_FINISHING_SECONDS).isAfter(LocalDateTime.now())) {
    +				// do nothing
    +				// this.state = State.FINISH_ERROR_HANDLING;
    +			} else {
    +				this.initialize();
    +				return StateMachine.State.UNDEFINED;
    +			}
    +		}
    +
    +		return StateMachine.State.ERROR;
    +	}
    +
    +	private State doLinkVoltageTooLow() throws IllegalArgumentException, OpenemsNamedException {
    +
    +		new CommandControlRegisters() //
    +				// Stop the system
    +				.stop(true) //
    +				.syncApproval(true) //
    +				.blackstartApproval(false) //
    +				.shortCircuitHandling(true) //
    +				.modeSelection(CommandControlRegisters.Mode.CURRENT_CONTROL) //
    +				.parameterSet1(true) //
    +				.parameterU0(GridconPCS.ON_GRID_VOLTAGE_FACTOR) //
    +				.parameterF0(GridconPCS.ON_GRID_FREQUENCY_FACTOR) //
    +				.enableIpus(this.parent.parent.config.inverterCount()) //
    +				.writeToChannels(this.parent.parent);
    +
    +		return State.FINISH_ERROR_HANDLING;
    +	}
    +
    +	/**
    +	 * Reads all active Errors into 'readErrorMap'.
    +	 * 
    +	 * @return the next state
    +	 */
    +	private State doReadErrors() {
    +		if (this.readErrorMap == null) {
    +			this.readErrorMap = new HashMap<>();
    +		}
    +
    +		ChannelId errorId = this.readCurrentError();
    +		if (errorId != null) {
    +			if (!this.readErrorMap.containsKey(errorId)) {
    +				this.readErrorMap.put(errorId, LocalDateTime.now());
    +			}
    +		}
    +
    +		// Did errors appear within the last DELAY_TO_WAIT_FOR_NEW_ERROR_SECONDS?
    +		if (this.isNewErrorPresent()) {
    +			// yes -> keep reading errors
    +			return State.READ_ERRORS;
    +		} else {
    +			// no -> start handling errors
    +			return State.HANDLE_ERRORS;
    +		}
    +	}
    +
    +	/**
    +	 * Handle Errors.
    +	 * 
    +	 * 
      + *
    • Errors are acknowledgeable and we did not try too often -> switch to + * ACKNOWLEDGE_ERRORS state + *
    • Otherwise -> switch to HARD_RESET state + *
    • If Hard-Reset did not work for MAX_TIMES_FOR_TRYING_TO_HARD_RESET times + * -> switch to ERROR_HANDLING_NOT_POSSIBLE + *
    + * + * @return the next state + */ + private State doHandleErrors() { + if (this.isAcknowledgeable() + && this.tryToAcknowledgeErrorsCounter < MAX_TIMES_FOR_TRYING_TO_ACKNOWLEDGE_ERRORS) { + // All errors are acknowledgeable and we did not try too often to acknowledge + // them -> switch to ACKNOWLEDGE_ERRORS state + return State.ACKNOWLEDGE_ERRORS; + + } else if (this.hardResetCounter < MAX_TIMES_FOR_TRYING_TO_HARD_RESET) { + // At least one error is not acknowledgeable -> hard reset + this.tryToAcknowledgeErrorsCounter = 0; // reset acknowledge-counter + return State.HARD_RESET; + + } else { + return State.ERROR_HANDLING_NOT_POSSIBLE; + } + } + + /** + * Acknowledges errors. + * + *

    + * Writes read errors to error code feedback. If all errors are written to error + * code feedback continue normal operation. + * + * @throws IllegalArgumentException + * @throws OpenemsNamedException + */ + private State doAcknowledgeErrors() throws IllegalArgumentException, OpenemsNamedException { + this.tryToAcknowledgeErrorsCounter = this.tryToAcknowledgeErrorsCounter + 1; + + int currentErrorCodeFeedBack = 0; + + if (!readErrorMap.isEmpty()) { + io.openems.edge.common.channel.ChannelId currentId = null; + for (io.openems.edge.common.channel.ChannelId id : this.readErrorMap.keySet()) { + currentId = id; + break; + } + currentErrorCodeFeedBack = ((ErrorDoc) currentId.doc()).getCode(); + this.readErrorMap.remove(currentId); + } + + new CommandControlRegisters() // + // Acknowledge error + .acknowledge(true) // + .syncApproval(true) // + .blackstartApproval(false) // + .errorCodeFeedback(currentErrorCodeFeedBack) // + .shortCircuitHandling(true) // + .modeSelection(CommandControlRegisters.Mode.CURRENT_CONTROL) // + .parameterSet1(true) // + .parameterU0(GridconPCS.ON_GRID_VOLTAGE_FACTOR) // + .parameterF0(GridconPCS.ON_GRID_FREQUENCY_FACTOR) // + .enableIpus(this.parent.parent.config.inverterCount()) // + .writeToChannels(this.parent.parent); + + if (this.readErrorMap.isEmpty()) { + + if (!this.sendSecondAcknowledge) { + new CommandControlRegisters() // + // Set acknowledge error bit to false and then to true again, because gridcon + // acts on rising edge of signal + .acknowledge(false) // + .syncApproval(true) // + .blackstartApproval(false) // + .errorCodeFeedback(currentErrorCodeFeedBack) // + .shortCircuitHandling(true) // + .modeSelection(CommandControlRegisters.Mode.CURRENT_CONTROL) // + .parameterSet1(true) // + .parameterU0(GridconPCS.ON_GRID_VOLTAGE_FACTOR) // + .parameterF0(GridconPCS.ON_GRID_FREQUENCY_FACTOR) // + .enableIpus(this.parent.parent.config.inverterCount()) // + .writeToChannels(this.parent.parent); + + this.sendSecondAcknowledge = true; + + } else { + + new CommandControlRegisters() // + // Acknowledge error for a 2nd time (in tests gridcon needs sometime two times) + .acknowledge(true) // + .syncApproval(true) // + .blackstartApproval(false) // + .errorCodeFeedback(currentErrorCodeFeedBack) // + .shortCircuitHandling(true) // + .modeSelection(CommandControlRegisters.Mode.CURRENT_CONTROL) // + .parameterSet1(true) // + .parameterU0(GridconPCS.ON_GRID_VOLTAGE_FACTOR) // + .parameterF0(GridconPCS.ON_GRID_FREQUENCY_FACTOR) // + .enableIpus(this.parent.parent.config.inverterCount()) // + .writeToChannels(this.parent.parent); + + this.sendSecondAcknowledge = false; + this.timeWhenErrorsHasBeenAcknowledged = LocalDateTime.now(); + return State.FINISH_ERROR_HANDLING; + } + } + return State.ACKNOWLEDGE_ERRORS; + } + + /** + * Execute a Hard-Reset, i.e. switch the Gridcon PCS off and on. + * + * @return the next state + * @throws IllegalArgumentException + * @throws OpenemsNamedException + */ + private State doHardReset() throws IllegalArgumentException, OpenemsNamedException { + if (this.lastHardReset == null) { + // Start Hard-Reset -> close the contactor + this.hardResetCounter = this.hardResetCounter + 1; + this.lastHardReset = LocalDateTime.now(); + this.parent.parent.setHardResetContactor(true); + return State.HARD_RESET; + } + + if (this.lastHardReset.plusSeconds(SWITCH_OFF_TIME_SECONDS).isAfter(LocalDateTime.now())) { + // just wait and keep the contactor closed + this.parent.parent.setHardResetContactor(true); + return State.HARD_RESET; + } + + if (this.lastHardReset.plusSeconds(SWITCH_OFF_TIME_SECONDS + RELOAD_TIME_SECONDS) + .isAfter(LocalDateTime.now())) { + // switch-off-time passed -> Open the contactor + this.parent.parent.setHardResetContactor(false); + return State.HARD_RESET; + } + + // switch-off-time and reload-time passed + this.parent.parent.setHardResetContactor(false); // Keep contactor open + // Mr Gridcon should be back, so reset everything to start conditions + this.lastHardReset = null; + return State.FINISH_ERROR_HANDLING; + } + + /** + * Impossible do manually handle this error. Wait for human help. + * + * @return + */ + private State doErrorHandlingNotPossible() { + // TODO switch off system + this.parent.parent.channel(GridConChannelId.STATE_CYCLE_ERROR).setNextValue(true); + return State.ERROR_HANDLING_NOT_POSSIBLE; + } + + private io.openems.edge.common.channel.ChannelId readCurrentError() { + StateChannel errorChannel = this.getErrorChannel(); + if (errorChannel != null) { + return errorChannel.channelId(); + } + return null; + } + + /** + * Gets the (first) active Error-Channel; or null if no Error is present. + * + * @return the Error-Channel or null + */ + protected StateChannel getErrorChannel() { + IntegerReadChannel errorCodeChannel = this.parent.parent.channel(GridConChannelId.CCU_ERROR_CODE); + Optional errorCodeOpt = errorCodeChannel.value().asOptional(); + if (errorCodeOpt.isPresent() && errorCodeOpt.get() != 0) { + int code = errorCodeOpt.get(); + code = code >> 8; + ChannelId id = this.errorChannelIds.get(code); + this.log.info("Error code is present --> " + code + " --> " + ((ErrorDoc) id.doc()).getText()); + return this.parent.parent.channel(id); + } + return null; + } + + /** + * Is any new error present?. + * + * @return true if a a new error was found recently; otherwise false + */ + private boolean isNewErrorPresent() { + for (LocalDateTime time : this.readErrorMap.values()) { + if (time.plusSeconds(DELAY_TO_WAIT_FOR_NEW_ERROR_SECONDS).isAfter(LocalDateTime.now())) { + return true; + } + } + return false; + } + + /** + * Are all errors acknowledgeable, i.e. none of them requires a Hard-Reset. + * + * @return true if all errors are acknowledgeable; false otherwise + */ + private boolean isAcknowledgeable() { + for (ChannelId id : readErrorMap.keySet()) { + for (ChannelId id2 : this.errorChannelIds.values()) { + if (id.equals(id2)) { + if (id instanceof ErrorCodeChannelId0) { + if (((ErrorDoc) ((ErrorCodeChannelId0) id).doc()).isNeedsHardReset()) { + return false; + } + } else if (id instanceof ErrorCodeChannelId1) { + if (((ErrorDoc) ((ErrorCodeChannelId1) id).doc()).isNeedsHardReset()) { + return false; + } + } + } + } + } + return true; + } + + private Map fillErrorChannelMap() { + Map result = new HashMap<>(); + for (ChannelId id : ErrorCodeChannelId0.values()) { + result.put(((ErrorDoc) id.doc()).getCode(), id); + } + for (ChannelId id : ErrorCodeChannelId1.values()) { + result.put(((ErrorDoc) id.doc()).getCode(), id); + } + return result; + } + + public enum State implements OptionsEnum { + UNDEFINED(-1, "Undefined"), // + READ_ERRORS(1, "Read Errors"), // + ACKNOWLEDGE_ERRORS(2, "Acknowledge Errors"), // + HANDLE_ERRORS(3, "Handle Errors"), // + HARD_RESET(4, "Hard Reset"), // + FINISH_ERROR_HANDLING(5, "Finish Error Handling"), // + ERROR_HANDLING_NOT_POSSIBLE(6, "Error handling not possible"), LINK_VOLTAGE_TOO_LOW(7, "Link voltage too low"); + + private final int value; + private final String name; + + private State(int value, String name) { + this.value = value; + this.name = name; + } + + @Override + public int getValue() { + return value; + } + + @Override + public String getName() { + return name; + } + + @Override + public OptionsEnum getUndefined() { + return UNDEFINED; + } + } + +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GoingOffgridHandler.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GoingOffgridHandler.java new file mode 100644 index 00000000000..af4eb16e85f --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GoingOffgridHandler.java @@ -0,0 +1,109 @@ +package io.openems.edge.ess.mr.gridcon; + +import java.time.LocalDateTime; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.types.OptionsEnum; + +public class GoingOffgridHandler { + + private final Logger log = LoggerFactory.getLogger(GoingOffgridHandler.class); + @SuppressWarnings("unused") + private final StateMachine parent; + + private State state = State.UNDEFINED; + + // WAIT + private LocalDateTime startedWaiting = null; + private final static int WAIT_SECONDS = 5; + + public GoingOffgridHandler(StateMachine parent) { + this.parent = parent; + } + + public void initialize() { + this.state = State.UNDEFINED; + this.startedWaiting = null; + } + + protected StateMachine.State run() { + System.out.println("GoingOffgridHandler.run"); + switch (this.state) { + case UNDEFINED: + this.state = this.doUndefined(); + break; + + case WAIT: + this.state = this.doWait(); + break; + + case FINISH_GOING_OFFGRID: + // finish GoingOffgridHandler, switch to OFFGRID-State + this.initialize(); + return StateMachine.State.OFFGRID; + } + + return StateMachine.State.GOING_OFFGRID; + } + + /** + * Handle UNDEFINED, i.e. GoingOffgridHandler just started taking over. Starts + * with WAIT-State. + * + * @return the next state + */ + private State doUndefined() { + return State.WAIT; + } + + /** + * Handle WAIT. Waits WAIT_SECONDS, then switches to FINISH_GOING_OFFGRID + * + * @return the next state + */ + private State doWait() { + if (this.startedWaiting == null) { + this.startedWaiting = LocalDateTime.now(); + } + + if (this.startedWaiting.plusSeconds(WAIT_SECONDS).isAfter(LocalDateTime.now())) { + this.log.info("doWaitFirstSeconds() waiting the first seconds"); + return State.WAIT; + } + + // finished waiting + this.log.info("doWaitFirstSeconds() finished waiting"); + return State.FINISH_GOING_OFFGRID; + } + + public enum State implements OptionsEnum { + UNDEFINED(-1, "Undefined"), // + WAIT(1, "For the first seconds just wait"), // + FINISH_GOING_OFFGRID(3, "Finish Going Off-Grid"); // + + private final int value; + private final String name; + + private State(int value, String name) { + this.value = value; + this.name = name; + } + + @Override + public int getValue() { + return value; + } + + @Override + public String getName() { + return name; + } + + @Override + public OptionsEnum getUndefined() { + return UNDEFINED; + } + } +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GoingOngridHandler.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GoingOngridHandler.java new file mode 100644 index 00000000000..f3252b452d5 --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GoingOngridHandler.java @@ -0,0 +1,115 @@ +package io.openems.edge.ess.mr.gridcon; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.types.ChannelAddress; +import io.openems.edge.common.channel.BooleanReadChannel; +import io.openems.edge.ess.mr.gridcon.enums.InverterCount; +import io.openems.edge.ess.mr.gridcon.enums.PControlMode; +import io.openems.edge.ess.mr.gridcon.writeutils.CcuControlParameters; +import io.openems.edge.ess.mr.gridcon.writeutils.CommandControlRegisters; +import io.openems.edge.meter.api.SymmetricMeter; + +public class GoingOngridHandler { + + private final Logger log = LoggerFactory.getLogger(GoingOngridHandler.class); + + private final StateMachine parent; + + public GoingOngridHandler(StateMachine parent) { + this.parent = parent; + } + + public void initialize() { + } + + protected StateMachine.State run() throws IllegalArgumentException, OpenemsNamedException { + System.out.println("GoingOngridHandler.run"); + + + // Are we still Off-Grid? + BooleanReadChannel inputNAProtection1 = parent.parent.componentManager + .getChannel(ChannelAddress.fromString(parent.parent.config.inputNAProtection1())); + BooleanReadChannel inputNAProtection2 = parent.parent.componentManager + .getChannel(ChannelAddress.fromString(parent.parent.config.inputNAProtection2())); + + Optional isInputNAProtection1 = inputNAProtection1.value().asOptional(); + Optional isInputNAProtection2 = inputNAProtection2.value().asOptional(); + + if (isInputNAProtection1.isPresent() && isInputNAProtection1.get()) { + + if (isInputNAProtection2.isPresent() && isInputNAProtection2.get()) { + return StateMachine.State.ONGRID; + } + } else { + return StateMachine.State.OFFGRID; + } + + this.doBlackStartGoingOnGrid(); + + return StateMachine.State.GOING_ONGRID; + } + + /** + * Handle BlackStart GoingOnGrid. + * + * @param gridFreq + * @param gridVolt + * @throws IllegalArgumentException + * @throws OpenemsNamedException + */ + private void doBlackStartGoingOnGrid() throws IllegalArgumentException, OpenemsNamedException { + + // Always set OutputSyncDeviceBridge ON in Off-Grid state + this.parent.parent.setOutputSyncDeviceBridge(true); + + SymmetricMeter gridMeter = this.parent.parent.componentManager.getComponent(this.parent.parent.config.meter()); + + Optional gridFreqOpt = gridMeter.getFrequency().value().asOptional(); + Optional gridVoltOpt = gridMeter.getVoltage().value().asOptional(); + + this.log.info("GoingOngridHandler.doBlackStartGoingOnGrid() GridFreq: " + gridFreqOpt + ", GridVolt: " + gridVoltOpt); + + if (!gridFreqOpt.isPresent() || !gridVoltOpt.isPresent()) { + // Cannot set anything without values + return; + } + + int gridFreq = gridFreqOpt.get(); + int gridVolt = gridVoltOpt.get(); + + int invSetFreq = gridFreq + this.parent.parent.config.overFrequency(); + int invSetVolt = gridVolt + this.parent.parent.config.overVoltage(); + + float invSetFreqNormalized = invSetFreq / 50_000f; + float invSetVoltNormalized = invSetVolt / 231_000f; + + log.info("OffgridHandler.doBlackStartGoingOnGrid() Going On-Grid -> F/U " + invSetFreq + ", " + invSetVolt + + ", " + invSetFreqNormalized + ", " + invSetVoltNormalized); + + InverterCount inverterCount = this.parent.parent.config.inverterCount(); + new CommandControlRegisters() // + .play(true) // + .ready(false) // + .acknowledge(false) // + .stop(false) // + .syncApproval(false) // + .blackstartApproval(true) // + .shortCircuitHandling(false) // + .modeSelection(CommandControlRegisters.Mode.VOLTAGE_CONTROL) // + .enableIpus(inverterCount) // + .parameterU0(invSetVoltNormalized) // + .parameterF0(invSetFreqNormalized) // + .writeToChannels(this.parent.parent); + new CcuControlParameters() // + .pControlMode(PControlMode.DISABLED) // + .qLimit(1f) // + .writeToChannels(this.parent.parent); + this.parent.parent.setIpuControlSettings(); + } + +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPCS.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPCS.java index 4e57b842f95..db25da94c1a 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPCS.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPCS.java @@ -1,11 +1,7 @@ package io.openems.edge.ess.mr.gridcon; -import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.BitSet; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; import java.util.function.Consumer; @@ -26,14 +22,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; import io.openems.edge.battery.api.Battery; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; 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.element.UnsignedDoublewordElement; @@ -44,9 +41,7 @@ import io.openems.edge.common.channel.BooleanReadChannel; import io.openems.edge.common.channel.BooleanWriteChannel; import io.openems.edge.common.channel.FloatReadChannel; -import io.openems.edge.common.channel.IntegerReadChannel; -import io.openems.edge.common.channel.StateChannel; -import io.openems.edge.common.channel.WriteChannel; +import io.openems.edge.common.channel.FloatWriteChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; @@ -58,18 +53,17 @@ import io.openems.edge.common.taskmanager.Priority; import io.openems.edge.ess.api.ManagedSymmetricEss; import io.openems.edge.ess.api.SymmetricEss; -import io.openems.edge.ess.mr.gridcon.enums.CCUState; -import io.openems.edge.ess.mr.gridcon.enums.ErrorCodeChannelId; +import io.openems.edge.ess.mr.gridcon.enums.ErrorCodeChannelId0; import io.openems.edge.ess.mr.gridcon.enums.ErrorCodeChannelId1; -import io.openems.edge.ess.mr.gridcon.enums.ErrorDoc; import io.openems.edge.ess.mr.gridcon.enums.GridConChannelId; -import io.openems.edge.ess.mr.gridcon.enums.PControlMode; +import io.openems.edge.ess.mr.gridcon.enums.InverterCount; +import io.openems.edge.ess.mr.gridcon.writeutils.DcdcControl; +import io.openems.edge.ess.mr.gridcon.writeutils.IpuInverterControl; import io.openems.edge.ess.power.api.Constraint; import io.openems.edge.ess.power.api.Phase; import io.openems.edge.ess.power.api.Power; import io.openems.edge.ess.power.api.Pwr; import io.openems.edge.ess.power.api.Relationship; -import io.openems.edge.meter.api.SymmetricMeter; @Designate(ocd = Config.class, factory = true) @Component( // @@ -81,17 +75,23 @@ public class GridconPCS extends AbstractOpenemsModbusComponent implements ManagedSymmetricEss, SymmetricEss, OpenemsComponent, EventHandler, ModbusSlave { - private static final int GRIDCON_SWITCH_OFF_TIME_SECONDS = 15; - private static final int GRIDCON_BOOT_TIME_SECONDS = 30; - private static final int MAX_POWER_PER_INVERTER = 41_900; // experimentally measured - private static final float MAX_CHARGE_W = 86 * 1000; - private static final float MAX_DISCHARGE_W = 86 * 1000; + public static final float DC_LINK_VOLTAGE_SETPOINT = 800f; + public static final float DC_LINK_VOLTAGE_TOLERANCE_VOLT = 20; + public static final int MAX_POWER_PER_INVERTER = 41_900; // experimentally measured + + public static final float ON_GRID_FREQUENCY_FACTOR = 1.035f; + public static final float ON_GRID_VOLTAGE_FACTOR = 0.97f; + + protected static final float OFF_GRID_FREQUENCY_FACTOR = 1.012f; + protected static final float OFF_GRID_VOLTAGE_FACTOR = 1.0f; + private final Logger log = LoggerFactory.getLogger(GridconPCS.class); - private Config config; - private Map errorChannelIds = null; - private LocalDateTime timestampMrGridconWasSwitchedOff; + // State-Machines + private final StateMachine stateMachine; + + protected Config config; @Reference private Power power; @@ -102,19 +102,16 @@ public class GridconPCS extends AbstractOpenemsModbusComponent @Reference protected ComponentManager componentManager; - private LocalDateTime lastTimeAcknowledgeCommandoWasSent; - private long ACKNOWLEDGE_TIME_SECONDS = 5; - public GridconPCS() { super(// OpenemsComponent.ChannelId.values(), // SymmetricEss.ChannelId.values(), // ManagedSymmetricEss.ChannelId.values(), // - ErrorCodeChannelId.values(), // + ErrorCodeChannelId0.values(), // ErrorCodeChannelId1.values(), // GridConChannelId.values() // ); - fillErrorChannelMap(); + this.stateMachine = new StateMachine(this); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) @@ -127,601 +124,187 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept this.config = config; // Calculate max apparent power from number of inverters - this.getMaxApparentPower().setNextValue(config.inverterCount().getCount() * MAX_POWER_PER_INVERTER); + this.getMaxApparentPower().setNextValue(config.inverterCount().getMaxApparentPower()); // Call parent activate() - super.activate(context, config.id(), config.enabled(), config.unit_id(), this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), config.unit_id(), this.cm, "Modbus", + config.modbus_id()); + } + @Deactivate protected void deactivate() { super.deactivate(); } - private void fillErrorChannelMap() { - errorChannelIds = new HashMap<>(); - for (io.openems.edge.common.channel.ChannelId id : ErrorCodeChannelId.values()) { - errorChannelIds.put(((ErrorDoc) id.doc()).getCode(), id); - } - for (io.openems.edge.common.channel.ChannelId id : ErrorCodeChannelId1.values()) { - errorChannelIds.put(((ErrorDoc) id.doc()).getCode(), id); - } - } - - private void handleStateMachine() throws IllegalArgumentException, OpenemsNamedException { - this.prepareGeneralCommands(); - - GridMode gridMode = this.getGridMode().value().asEnum(); - switch (gridMode) { - case ON_GRID: - log.info("handleOnGridState"); - this.handleOnGridState(); - break; - case OFF_GRID: - log.info("handleOffGridState"); - this.handleOffGridState(); - break; - case UNDEFINED: - break; - } - } - - private void prepareGeneralCommands() throws OpenemsNamedException { - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_PLAY, false); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_READY, false); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE, false); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_STOP, false); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, true); - - // Enable DC DC - switch (this.config.inverterCount()) { - case ONE: - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, false); - break; - case TWO: - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, false); - break; - case THREE: - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, false); - break; - } - - writeValueToChannel(GridConChannelId.COMMAND_ERROR_CODE_FEEDBACK, 0); - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_Q_REF, 0); - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_P_REF, 0); - /** - * Always write values for frequency and voltage to gridcon, because in case of - * blackstart mode if we write '0' to gridcon the systems tries to regulate - * frequency and voltage to zero which would be bad for Mr. Gridcon's health - */ - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_U0, 1.0f); - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_F0, 1.035f); - writeDateAndTime(); - - writeCCUControlParameters(PControlMode.ACTIVE_POWER_CONTROL); - writeIPUParameters(1f, 1f, 1f, MAX_DISCHARGE_W, MAX_DISCHARGE_W, MAX_DISCHARGE_W, MAX_CHARGE_W, MAX_CHARGE_W, - MAX_CHARGE_W, getWeightingMode()); - } - - private float getWeightingMode() throws OpenemsNamedException { - - float weightingMode = 0; - - // Depends on number of battery strings!!! - // battA = 1 (2^0) - // battB = 8 (2^3) - // battC = 64 (2^6) - - Battery batteryStringA = this.componentManager.getComponent(this.config.batteryStringA_id()); - if (batteryStringA != null) { - weightingMode = weightingMode + 1; - } - Battery batteryStringB = this.componentManager.getComponent(this.config.batteryStringB_id()); - if (batteryStringB != null) { - weightingMode = weightingMode + 8; - } - Battery batteryStringC = this.componentManager.getComponent(this.config.batteryStringC_id()); - if (batteryStringC != null) { - weightingMode = weightingMode + 64; - } - - return weightingMode; - } - - private void handleOnGridState() throws IllegalArgumentException, OpenemsNamedException { - offGridDetected = null; - System.out.println(" ------ Currently set error channels ------- "); - for (io.openems.edge.common.channel.ChannelId id : errorChannelIds.values()) { - @SuppressWarnings("unchecked") - Optional val = (Optional) this.channel(id).value().asOptional(); - if (val.isPresent() && val.get()) { - System.out.println(this.channel(id).address().getChannelId() + " is present"); - } - } - // Always set OutputSyncDeviceBridge OFF in On-Grid state - this.setOutputSyncDeviceBridge(false); - - // a hardware restart has been executed, - if (timestampMrGridconWasSwitchedOff != null) { - log.info("timestampMrGridconWasSwitchedOff is set: " + timestampMrGridconWasSwitchedOff.toString()); - if ( // - LocalDateTime.now().isAfter(timestampMrGridconWasSwitchedOff.plusSeconds(GRIDCON_SWITCH_OFF_TIME_SECONDS)) && // - LocalDateTime.now().isBefore(timestampMrGridconWasSwitchedOff.plusSeconds(GRIDCON_SWITCH_OFF_TIME_SECONDS + GRIDCON_BOOT_TIME_SECONDS)) // - ) { - try { - log.info("try to write to channel hardware reset, set it to 'false'"); - // after 15 seconds switch Mr. Gridcon on again! - BooleanWriteChannel channelHardReset = this.componentManager.getChannel(ChannelAddress.fromString(this.config.outputMRHardReset())); - channelHardReset.setNextWriteValue(false); - resetErrorChannels(); - } catch (IllegalArgumentException | OpenemsNamedException e) { - log.error("Problem occurred while deactivating hardware switch!"); - e.printStackTrace(); - } - - } else if (LocalDateTime.now().isAfter(timestampMrGridconWasSwitchedOff - .plusSeconds(GRIDCON_SWITCH_OFF_TIME_SECONDS + GRIDCON_BOOT_TIME_SECONDS))) { - timestampMrGridconWasSwitchedOff = null; - } + /** + * The main entry point for the Gridcon PCS implementation. Everything starts at + * the TOPIC_CYCLE_AFTER_PROCESS_IMAGE event. + * + *

    + * Prepares some default channels, than handles the central State-Machine. + */ + @Override + public void handleEvent(Event event) { + if (!this.isEnabled()) { return; } + switch (event.getTopic()) { + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: + try { + // prepare calculated Channels + this.calculateGridMode(); + this.calculateBatteryData(); + this.calculateSoc(); - switch (getCurrentState()) { - case DERATING_HARMONICS: - break; - case DERATING_POWER: - break; - case ERROR: - doErrorHandling(); - break; - case IDLE: - startSystem(); - break; - case OVERLOAD: - break; - case PAUSE: - break; - case PRECHARGE: - break; - case READY: - break; - case RUN: - doRunHandling(); - break; - case SHORT_CIRCUIT_DETECTED: - break; - case SIA_ACTIVE: - break; - case STOP_PRECHARGE: - break; - case UNDEFINED: - break; - case VOLTAGE_RAMPING_UP: - break; - } - - resetErrorCodes(); - } - - private void resetErrorChannels() { - for (io.openems.edge.common.channel.ChannelId id : errorChannelIds.values()) { - this.channel(id).setNextValue(false); - } - } - - private void resetErrorCodes() { - IntegerReadChannel errorCodeChannel = this.channel(GridConChannelId.CCU_ERROR_CODE); - Optional errorCodeOpt = errorCodeChannel.value().asOptional(); - log.debug("in resetErrorCodes: => Errorcode: " + errorCodeOpt); - if (errorCodeOpt.isPresent() && errorCodeOpt.get() != 0) { - writeValueToChannel(GridConChannelId.COMMAND_ERROR_CODE_FEEDBACK, errorCodeOpt.get()); - } - } - - private void doRunHandling() throws OpenemsException { - resetErrorChannels(); // if any error channels has been set, unset them because in here there are no - // errors present ==> TODO EBEN NICHT!!! fall aufgetreten dass state RUN war - // aber ein fehler in der queue und das system nicht angelaufen ist.... - - boolean disableIpu1 = false; - boolean disableIpu2 = true; - boolean disableIpu3 = true; - boolean disableIpu4 = true; + // start state-machine handling + this.stateMachine.run(); - switch (this.config.inverterCount()) { - case ONE: - disableIpu2 = false; - break; - case TWO: - disableIpu2 = false; - disableIpu3 = false; - break; - case THREE: - disableIpu2 = false; - disableIpu3 = false; - disableIpu4 = false; + this.channel(GridConChannelId.STATE_CYCLE_ERROR).setNextValue(false); + } catch (IllegalArgumentException | OpenemsNamedException e) { + this.channel(GridConChannelId.STATE_CYCLE_ERROR).setNextValue(true); + this.logError(this.log, "State-Cycle Error: " + e.getMessage()); + } break; } - - // send play command - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_PLAY, true); //TODO just for testing purposes, because play is only necessary at startup - - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1, disableIpu1); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, disableIpu2); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, disableIpu3); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, disableIpu4); - } - - private void setNextWriteValueToBooleanWriteChannel(GridConChannelId id, boolean b) throws OpenemsException { - ((BooleanWriteChannel) this.channel(id)).setNextWriteValue(true); - } - private void writeCCUControlParameters(PControlMode mode) { - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_U_Q_DROOP_MAIN, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_U_Q_DROOP_T1_MAIN, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_F_P_DRROP_MAIN, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_F_P_DROOP_T1_MAIN, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_Q_U_DROOP_MAIN, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_Q_U_DEAD_BAND, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_Q_LIMIT, 1f); // 0 -> limits Q to zero, 1 -> to max Q - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_F_DROOP_MAIN, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_F_DEAD_BAND, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_U_DROOP, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_U_DEAD_BAND, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_U_MAX_CHARGE, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_U_MAX_DISCHARGE, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_CONTROL_LIM_TWO, 0f); - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_CONTROL_LIM_ONE, 0f); - // the only relevant parameter is 'P Control Mode' which should be set to - // 'Active power control' in case of on grid usage - writeValueToChannel(GridConChannelId.CONTROL_PARAMETER_P_CONTROL_MODE, mode.getFloatValue()); // + /** + * if parameter is true, the contactor will be closed if it's false, the + * contactor will be opened and Mr Gridcon is going to start + * + * @param closed + * @throws OpenemsNamedException + * @throws IllegalArgumentException + */ + public void setHardResetContactor(boolean closed) throws IllegalArgumentException, OpenemsNamedException { + BooleanWriteChannel channelHardReset = this.componentManager + .getChannel(ChannelAddress.fromString(this.config.outputMRHardReset())); + channelHardReset.setNextWriteValue(closed); } /** - * This writes the current time into the necessary channel for the - * communicationprotocol with the gridcon. + * Evaluates the Grid-Mode and sets the GRID_MODE channel accordingly. + * + * @return + * + * @throws OpenemsNamedException + * @throws IllegalArgumentException */ - private void writeDateAndTime() { - LocalDateTime time = LocalDateTime.now(); - byte dayOfWeek = (byte) time.getDayOfWeek().ordinal(); - byte day = (byte) time.getDayOfMonth(); - byte month = (byte) time.getMonth().getValue(); - byte year = (byte) (time.getYear() - 2000); // 0 == year 1900 in the protocol - - Integer dateInteger = convertToInteger(BitSet.valueOf(new byte[] { day, dayOfWeek, year, month })); - - byte seconds = (byte) time.getSecond(); - byte minutes = (byte) time.getMinute(); - byte hours = (byte) time.getHour(); + private void calculateGridMode() throws IllegalArgumentException, OpenemsNamedException { + GridMode gridMode = GridMode.UNDEFINED; + try { + BooleanReadChannel inputNAProtection1 = this.componentManager + .getChannel(ChannelAddress.fromString(this.config.inputNAProtection1())); + BooleanReadChannel inputNAProtection2 = this.componentManager + .getChannel(ChannelAddress.fromString(this.config.inputNAProtection2())); - // second byte is unused - Integer timeInteger = convertToInteger(BitSet.valueOf(new byte[] { seconds, 0, hours, minutes })); + Optional isInputNAProtection1 = inputNAProtection1.value().asOptional(); + Optional isInputNAProtection2 = inputNAProtection2.value().asOptional(); - writeValueToChannel(GridConChannelId.COMMAND_TIME_SYNC_DATE, dateInteger); - writeValueToChannel(GridConChannelId.COMMAND_TIME_SYNC_TIME, timeInteger); + if (!isInputNAProtection1.isPresent() || !isInputNAProtection2.isPresent()) { + gridMode = GridMode.UNDEFINED; + } else { + if (isInputNAProtection1.get() && isInputNAProtection2.get()) { + gridMode = GridMode.ON_GRID; + } else { + gridMode = GridMode.OFF_GRID; + } + } + } finally { + this.getGridMode().setNextValue(gridMode); + } } /** - * This turns on the system by enabling ALL IPUs. - * @throws OpenemsException + * Handles Battery data, i.e. setting allowed charge/discharge power. */ - private void startSystem() throws OpenemsException { - log.info("Try to start system"); - /* - * Coming from state idle first write 800V to IPU4 voltage setpoint, set "73" to - * DCDC String Control Mode of IPU4 and "1" to Weight String A, B, C ==> i.e. - * all 3 IPUs are weighted equally write -86000 to Pmax discharge Iref String A, - * B, C, write 86000 to Pmax Charge DCDC Str Mode of IPU 1, 2, 3 set P Control - * mode to "Act Pow Ctrl" (hex 4000 = mode power limiter, 0 = disabled, hex 3F80 - * = active power control) and Mode Sel to "Current Control" s--> ee pic - * start0.png in doc folder - * - * enable "Sync Approval" and "Ena IPU 4" and PLAY command -> system should - * change state to "RUN" --> see pic start1.png - * - * after that enable IPU 1, 2, 3, if they have reached state "RUN" (=14) power - * can be set (from 0..1 (1 = max system power = 125 kW) , i.e. 0,05 is equal to - * 6.250 W same for reactive power see pic start2.png - * - * "Normal mode" is reached now - */ - - // enable "Sync Approval" and "Ena IPU 4, 3, 2, 1" and PLAY command -> system - // should change state to "RUN" - - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_PLAY, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL, false); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, true); - - switch (this.config.inverterCount()) { - case ONE: - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, false); - break; - case TWO: - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, false); - break; - case THREE: - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, false); - break; + private void calculateBatteryData() { + int allowedCharge = 0; + int allowedDischarge = 0; + for (Battery battery : this.getBatteries()) { + allowedCharge += battery.getVoltage().value().orElse(0) * battery.getChargeMaxCurrent().value().orElse(0) + * -1; + allowedDischarge += battery.getVoltage().value().orElse(0) + * battery.getDischargeMaxCurrent().value().orElse(0); } + this.getAllowedCharge().setNextValue(allowedCharge); + this.getAllowedDischarge().setNextValue(allowedDischarge); } - // TODO Shutdown system -// private void stopSystem() { -// log.info("Try to stop system"); -// -// // disable "Sync Approval" and "Ena IPU 4, 3, 2, 1" and add STOP command -> -// // system should change state to "IDLE" -// commandControlWord.set(PCSControlWordBitPosition.STOP.getBitPosition(), true); -// commandControlWord.set(PCSControlWordBitPosition.SYNC_APPROVAL.getBitPosition(), false); -// commandControlWord.set(PCSControlWordBitPosition.BLACKSTART_APPROVAL.getBitPosition(), false); -// commandControlWord.set(PCSControlWordBitPosition.MODE_SELECTION.getBitPosition(), true); -// -// commandControlWord.set(PCSControlWordBitPosition.DISABLE_IPU_1.getBitPosition(), true); -// commandControlWord.set(PCSControlWordBitPosition.DISABLE_IPU_2.getBitPosition(), true); -// commandControlWord.set(PCSControlWordBitPosition.DISABLE_IPU_3.getBitPosition(), true); -// commandControlWord.set(PCSControlWordBitPosition.DISABLE_IPU_4.getBitPosition(), true); -// } - /** - * This converts a Bitset to its decimal value. Only works as long as the value - * of the Bitset does not exceed the range of an Integer. - * - * @param bitSet The Bitset which should be converted - * @return The converted Integer + * Calculates the State-of-charge of all Batteries; if all batteries are + * available. Otherwise sets UNDEFINED. */ - private Integer convertToInteger(BitSet bitSet) { - long[] l = bitSet.toLongArray(); - - if (l.length == 0) { - return 0; + private void calculateSoc() { + float sumTotalCapacity = 0; + float sumCurrentCapacity = 0; + for (Battery b : this.getBatteries()) { + Optional totalCapacityOpt = b.getCapacity().value().asOptional(); + Optional socOpt = b.getSoc().value().asOptional(); + if (!totalCapacityOpt.isPresent() || !socOpt.isPresent()) { + // if at least one Battery has no valid value -> set UNDEFINED + this.getSoc().setNextValue(null); + return; + } + int totalCapacity = totalCapacityOpt.get(); + int soc = socOpt.get(); + sumTotalCapacity += totalCapacity; + sumCurrentCapacity += totalCapacity * soc / 100.0; } - return (int) l[0]; + int soc = Math.round(sumCurrentCapacity * 100 / sumTotalCapacity); + this.getSoc().setNextValue(soc); } /** - * Writes parameters to all 4 IPUs !! Max charge/discharge power for IPUs always - * in absolute values !! + * Sets the IPU-settings for Inverter-Control and DCDC-Control. * - * @param stringControlMode + * @throws IllegalArgumentException + * @throws OpenemsNamedException */ - private void writeIPUParameters(float weightA, float weightB, float weightC, float pMaxDischargeIPU1, - float pMaxDischargeIPU2, float pMaxDischargeIPU3, float pMaxChargeIPU1, float pMaxChargeIPU2, - float pMaxChargeIPU3, float stringControlMode) { - - switch (this.config.inverterCount()) { + public void setIpuControlSettings() throws IllegalArgumentException, OpenemsNamedException { + InverterCount inverterCount = this.config.inverterCount(); + switch (inverterCount) { case ONE: - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_DC_VOLTAGE_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_DC_CURRENT_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_U0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_F0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, 0f); - - // Gridcon needs negative values for discharge values, positive values for - // charge values - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_MAX_DISCHARGE, -pMaxDischargeIPU1); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_MAX_CHARGE, pMaxChargeIPU1); + new IpuInverterControl() // + .pMaxCharge(inverterCount.getMaxApparentPower()) // + .pMaxDischarge(inverterCount.getMaxApparentPower() * -1) // + .writeToChannels(this, IpuInverterControl.Inverter.ONE); break; case TWO: - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_DC_VOLTAGE_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_DC_CURRENT_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_U0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_F0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, 0f); - - // Gridcon needs negative values for discharge values, positive values for - // charge values - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_MAX_DISCHARGE, -pMaxDischargeIPU1); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_MAX_CHARGE, pMaxChargeIPU1); - - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_DC_VOLTAGE_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_DC_CURRENT_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_U0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_F0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, 0f); - - // Gridcon needs negative values for discharge values, positive values for - // charge values - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_P_MAX_DISCHARGE, -pMaxDischargeIPU2); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_P_MAX_CHARGE, pMaxChargeIPU2); + new IpuInverterControl() // + .pMaxCharge(inverterCount.getMaxApparentPower()) // + .pMaxDischarge(inverterCount.getMaxApparentPower() * -1) // + .writeToChannels(this, IpuInverterControl.Inverter.ONE) // + .writeToChannels(this, IpuInverterControl.Inverter.TWO); // break; case THREE: - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_DC_VOLTAGE_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_DC_CURRENT_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_U0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_F0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, 0f); - - // Gridcon needs negative values for discharge values, positive values for - // charge values - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_MAX_DISCHARGE, -pMaxDischargeIPU1); - writeValueToChannel(GridConChannelId.INVERTER_1_CONTROL_P_MAX_CHARGE, pMaxChargeIPU1); - - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_DC_VOLTAGE_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_DC_CURRENT_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_U0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_F0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, 0f); - - // Gridcon needs negative values for discharge values, positive values for - // charge values - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_P_MAX_DISCHARGE, -pMaxDischargeIPU2); - writeValueToChannel(GridConChannelId.INVERTER_2_CONTROL_P_MAX_CHARGE, pMaxChargeIPU2); - - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_DC_VOLTAGE_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_DC_CURRENT_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_U0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_F0_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, 0f); - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, 0f); - - // Gridcon needs negative values for discharge values, positive values for - // charge values - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_P_MAX_DISCHARGE, -pMaxDischargeIPU3); - writeValueToChannel(GridConChannelId.INVERTER_3_CONTROL_P_MAX_CHARGE, pMaxChargeIPU3); + new IpuInverterControl() // + .pMaxCharge(inverterCount.getMaxApparentPower()) // + .pMaxDischarge(inverterCount.getMaxApparentPower() * -1) // + .writeToChannels(this, IpuInverterControl.Inverter.ONE) // + .writeToChannels(this, IpuInverterControl.Inverter.TWO) // + .writeToChannels(this, IpuInverterControl.Inverter.THREE); break; } - - writeValueToChannel(GridConChannelId.DCDC_CONTROL_DC_VOLTAGE_SETPOINT, 0f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_A, 0f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_B, 0f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_C, 0f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_I_REF_STRING_A, 0f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_I_REF_STRING_B, 0f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_I_REF_STRING_C, 0f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_STRING_CONTROL_MODE, 0f); // - - // The value of 800 Volt is given by MR as a good reference value - writeValueToChannel(GridConChannelId.DCDC_CONTROL_DC_VOLTAGE_SETPOINT, 800f); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_A, weightA); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_B, weightB); - writeValueToChannel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_C, weightC); - // The value '73' implies that all 3 strings are in weighting mode - writeValueToChannel(GridConChannelId.DCDC_CONTROL_STRING_CONTROL_MODE, stringControlMode); // - } - - private void doErrorHandling() throws OpenemsException { - StateChannel c = getErrorChannel(); - if (c == null) { - System.out.println("Channel is null......"); - return; - } - c.setNextValue(true); - if (((ErrorDoc) c.channelId().doc()).isNeedsHardReset()) { - doHardRestart(); - } else { - log.info("try to acknowledge errors"); - acknowledgeErrors(); - } - } - - private StateChannel getErrorChannel() { - IntegerReadChannel errorCodeChannel = this.channel(GridConChannelId.CCU_ERROR_CODE); - Optional errorCodeOpt = errorCodeChannel.value().asOptional(); - if (errorCodeOpt.isPresent() && errorCodeOpt.get() != 0) { - int code = errorCodeOpt.get(); - System.out.println("Code read: " + code + " ==> hex: " + Integer.toHexString(code)); - code = code >> 8; - System.out.println("Code >> 8 read: " + code + " ==> hex: " + Integer.toHexString(code)); - log.info("Error code is present --> " + code); - io.openems.edge.common.channel.ChannelId id = errorChannelIds.get(code); - return this.channel(id); - } - return null; - } - - private void doHardRestart() { - try { - log.info("in doHardRestart"); - if (timestampMrGridconWasSwitchedOff == null) { - log.info("timestampMrGridconWasSwitchedOff was not set yet! try to write 'true' to channelHardReset!"); - BooleanWriteChannel channelHardReset = this.componentManager - .getChannel(ChannelAddress.fromString(this.config.outputMRHardReset())); - channelHardReset.setNextWriteValue(true); - timestampMrGridconWasSwitchedOff = LocalDateTime.now(); - } - } catch (IllegalArgumentException | OpenemsNamedException e) { - log.error("Problem occurred while activating hardware switch to restart Mr. Gridcon!"); - e.printStackTrace(); - } - - } - - /** - * This sends an ACKNOWLEDGE message. This does not fix the error. If the error - * was fixed previously the system should continue operating normally. If not a - * manual restart may be necessary. - * @throws OpenemsException - */ - private void acknowledgeErrors() throws OpenemsException { - if ( // - lastTimeAcknowledgeCommandoWasSent == null || // - LocalDateTime.now().isAfter(lastTimeAcknowledgeCommandoWasSent.plusSeconds(ACKNOWLEDGE_TIME_SECONDS)) // - ) { - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE, true); - lastTimeAcknowledgeCommandoWasSent = LocalDateTime.now(); - } + new DcdcControl() // + .dcVoltageSetpoint(GridconPCS.DC_LINK_VOLTAGE_SETPOINT) // + .stringControlMode(this.componentManager, this.config) // + .writeToChannels(this); } @Override public String debugLog() { - return "State:" + this.getCurrentState().toString() + "," + "L:" - + this.channel(SymmetricEss.ChannelId.ACTIVE_POWER).value().asString() // - + "," + this.getGridMode().value().asEnum().getName(); - } - - private CCUState getCurrentState() { - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_IDLE)).value().asOptional().orElse(false)) { - return CCUState.IDLE; - } - - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_PRECHARGE)).value().asOptional() - .orElse(false)) { - return CCUState.PRECHARGE; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_STOP_PRECHARGE)).value().asOptional() - .orElse(false)) { - return CCUState.STOP_PRECHARGE; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_READY)).value().asOptional().orElse(false)) { - return CCUState.READY; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_PAUSE)).value().asOptional().orElse(false)) { - return CCUState.PAUSE; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_RUN)).value().asOptional().orElse(false)) { - return CCUState.RUN; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_ERROR)).value().asOptional().orElse(false)) { - return CCUState.ERROR; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_VOLTAGE_RAMPING_UP)).value().asOptional() - .orElse(false)) { - return CCUState.VOLTAGE_RAMPING_UP; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_OVERLOAD)).value().asOptional() - .orElse(false)) { - return CCUState.OVERLOAD; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_SHORT_CIRCUIT_DETECTED)).value().asOptional() - .orElse(false)) { - return CCUState.SHORT_CIRCUIT_DETECTED; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_DERATING_POWER)).value().asOptional() - .orElse(false)) { - return CCUState.DERATING_POWER; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_DERATING_HARMONICS)).value().asOptional() - .orElse(false)) { - return CCUState.DERATING_HARMONICS; - } - if (((BooleanReadChannel) this.channel(GridConChannelId.CCU_STATE_SIA_ACTIVE)).value().asOptional() - .orElse(false)) { - return CCUState.SIA_ACTIVE; - } - - return CCUState.UNDEFINED; + return "SoC:" + this.getSoc().value().asString() // + + "|L:" + this.getActivePower().value().asString() // + + "|Allowed:" + + this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER).value().asStringWithoutUnit() + ";" + + this.channel(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER).value().asString() // + + "|" + this.getGridMode().value().asOptionString(); } @Override @@ -731,55 +314,80 @@ public Power getPower() { @Override public Constraint[] getStaticConstraints() { - GridMode gridMode = this.getGridMode().value().asEnum(); - if (getCurrentState() != CCUState.RUN || gridMode != GridMode.ON_GRID) { + if (this.stateMachine.getState() != StateMachine.State.ONGRID) { return new Constraint[] { this.createPowerConstraint("Inverter not ready", Phase.ALL, Pwr.ACTIVE, Relationship.EQUALS, 0), this.createPowerConstraint("Inverter not ready", Phase.ALL, Pwr.REACTIVE, Relationship.EQUALS, 0) }; - } else { - return Power.NO_CONSTRAINTS; } + return Power.NO_CONSTRAINTS; } @Override public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException { - doStringWeighting(activePower, reactivePower); - - Optional maxApparentPower = this.getMaxApparentPower().value().asOptional(); - float activePowerFactor = 0f; - float reactivePowerFactor = 0f; - if (maxApparentPower.isPresent()) { - /* - * !! signum, MR calculates negative values as discharge, positive as charge. - * Gridcon sets the (dis)charge according to a percentage of the - * MAX_APPARENT_POWER. So 0.1 => 10% of max power. Values should never take - * values lower than -1 or higher than 1. - */ - activePowerFactor = -activePower / maxApparentPower.get(); - reactivePowerFactor = -reactivePower / maxApparentPower.get(); + if (this.stateMachine.getOngridHandler().getState() != OngridHandler.State.RUN) { + // stop if not in On-Grid and Running -> Pref and Qref = 0 by + // CommandControlRegisters + return; } - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_P_REF, activePowerFactor); - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_Q_REF, reactivePowerFactor); + + // calculate and set the weights for battery strings A, B and C. + this.weightBatteryStrings(activePower); + + float maxApparentPower = this.config.inverterCount().getMaxApparentPower(); + /* + * !! signum, MR calculates negative values as discharge, positive as charge. + * Gridcon sets the (dis)charge according to a percentage of the + * MAX_APPARENT_POWER. So 0.1 => 10% of max power. Values should never take + * values lower than -1 or higher than 1. + */ + float activePowerFactor = -activePower / maxApparentPower; + float reactivePowerFactor = -reactivePower / maxApparentPower; + + FloatWriteChannel pRefChannel = this.channel(GridConChannelId.COMMAND_CONTROL_PARAMETER_P_REF); + FloatWriteChannel qRefChannel = this.channel(GridConChannelId.COMMAND_CONTROL_PARAMETER_Q_REF); + pRefChannel.setNextWriteValue(activePowerFactor); + qRefChannel.setNextWriteValue(reactivePowerFactor); } - private void doStringWeighting(int activePower, int reactivePower) throws OpenemsNamedException { + /** + * Sets the weights for battery strings A, B and C according to max allowed + * current. + * + * @param activePower + * @throws OpenemsNamedException + */ + private void weightBatteryStrings(int activePower) throws OpenemsNamedException { int weightA = 0; int weightB = 0; int weightC = 0; - Battery batteryStringA = this.componentManager.getComponent(this.config.batteryStringA_id()); - Battery batteryStringB = this.componentManager.getComponent(this.config.batteryStringB_id()); - Battery batteryStringC = this.componentManager.getComponent(this.config.batteryStringC_id()); + Battery batteryStringA = null; + Battery batteryStringB = null; + Battery batteryStringC = null; - // weight strings according to max allowed current - // use values for discharging - // weight zero power as discharging + try { + batteryStringA = this.componentManager.getComponent(this.config.batteryStringA_id()); + } catch (Exception e) { + // Exception needs not to be handled because batteries are allowed to be null + } + try { + batteryStringB = this.componentManager.getComponent(this.config.batteryStringB_id()); + } catch (Exception e) { + // Exception needs not to be handled because batteries are allowed to be null + } + try { + batteryStringC = this.componentManager.getComponent(this.config.batteryStringC_id()); + } catch (Exception e) { + // Exception needs not to be handled because batteries are allowed to be null + } if (activePower > 0) { - + /* + * Discharge + */ if (batteryStringA != null) { weightA = batteryStringA.getDischargeMaxCurrent().value().asOptional().orElse(0); // if minSoc is reached, do not allow further discharging - if (batteryStringA.getSoc().value().asOptional().orElse(0) <= this.config.minSocA()) { + if (batteryStringA.getSoc().value().asOptional().orElse(0) <= this.config.minSocBatteryA()) { weightA = 0; } } @@ -787,7 +395,7 @@ private void doStringWeighting(int activePower, int reactivePower) throws Openem if (batteryStringB != null) { weightB = batteryStringB.getDischargeMaxCurrent().value().asOptional().orElse(0); // if minSoc is reached, do not allow further discharging - if (batteryStringB.getSoc().value().asOptional().orElse(0) <= this.config.minSocB()) { + if (batteryStringB.getSoc().value().asOptional().orElse(0) <= this.config.minSocBatteryB()) { weightB = 0; } } @@ -795,11 +403,15 @@ private void doStringWeighting(int activePower, int reactivePower) throws Openem if (batteryStringC != null) { weightC = batteryStringC.getDischargeMaxCurrent().value().asOptional().orElse(0); // if minSoc is reached, do not allow further discharging - if (batteryStringC.getSoc().value().asOptional().orElse(0) <= this.config.minSocC()) { + if (batteryStringC.getSoc().value().asOptional().orElse(0) <= this.config.minSocBatteryC()) { weightC = 0; } } - } else if (activePower < 0) { // use values for charging + + } else if (activePower < 0) { + /* + * Charge + */ if (batteryStringA != null) { weightA = batteryStringA.getChargeMaxCurrent().value().asOptional().orElse(0); } @@ -809,261 +421,71 @@ private void doStringWeighting(int activePower, int reactivePower) throws Openem if (batteryStringC != null) { weightC = batteryStringC.getChargeMaxCurrent().value().asOptional().orElse(0); } - } else { // active power is zero + } else { + /* + * active power is zero + */ + + int factor = 100; if (batteryStringA != null && batteryStringB != null && batteryStringC != null) { // ABC Optional vAopt = batteryStringA.getVoltage().value().asOptional(); Optional vBopt = batteryStringB.getVoltage().value().asOptional(); Optional vCopt = batteryStringC.getVoltage().value().asOptional(); if (vAopt.isPresent() && vBopt.isPresent() && vCopt.isPresent()) { - int min = Math.min(vAopt.get(), Math.min(vBopt.get(), vCopt.get())); - weightA = vAopt.get() - min; - weightB = vBopt.get() - min; - weightC = vCopt.get() - min; + double averageVoltageA = vAopt.get() / this.config.weightFactorBatteryA(); + double averageVoltageB = vBopt.get() / this.config.weightFactorBatteryB(); + double averageVoltageC = vCopt.get() / this.config.weightFactorBatteryC(); + + double min = Math.min(averageVoltageA, Math.min(averageVoltageB, averageVoltageC)); + weightA = (int) ((averageVoltageA - min) * factor); + weightB = (int) ((averageVoltageB - min) * factor); + weightC = (int) ((averageVoltageC - min) * factor); } } else if (batteryStringA != null && batteryStringB != null && batteryStringC == null) { // AB Optional vAopt = batteryStringA.getVoltage().value().asOptional(); Optional vBopt = batteryStringB.getVoltage().value().asOptional(); if (vAopt.isPresent() && vBopt.isPresent()) { - int min = Math.min(vAopt.get(), vBopt.get()); - weightA = vAopt.get() - min; - weightB = vBopt.get() - min; + double averageVoltageA = vAopt.get() / this.config.weightFactorBatteryA(); + double averageVoltageB = vBopt.get() / this.config.weightFactorBatteryB(); + double min = Math.min(averageVoltageA, averageVoltageB); + weightA = (int) ((averageVoltageA - min) * factor); + weightB = (int) ((averageVoltageB - min) * factor); } } else if (batteryStringA != null && batteryStringB == null && batteryStringC != null) { // AC Optional vAopt = batteryStringA.getVoltage().value().asOptional(); Optional vCopt = batteryStringC.getVoltage().value().asOptional(); if (vAopt.isPresent() && vCopt.isPresent()) { - int min = Math.min(vAopt.get(), vCopt.get()); - weightA = vAopt.get() - min; - weightC = vCopt.get() - min; + double averageVoltageA = vAopt.get() / this.config.weightFactorBatteryA(); + double averageVoltageC = vCopt.get() / this.config.weightFactorBatteryC(); + double min = Math.min(averageVoltageA, averageVoltageC); + weightA = (int) ((averageVoltageA - min) * factor); + weightC = (int) ((averageVoltageC - min) * factor); } } else if (batteryStringA == null && batteryStringB != null && batteryStringC != null) { // BC Optional vBopt = batteryStringB.getVoltage().value().asOptional(); Optional vCopt = batteryStringC.getVoltage().value().asOptional(); if (vBopt.isPresent() && vCopt.isPresent()) { - int min = Math.min(vBopt.get(), vCopt.get()); - weightB = vBopt.get() - min; - weightC = vCopt.get() - min; + double averageVoltageB = vBopt.get() / this.config.weightFactorBatteryB(); + double averageVoltageC = vCopt.get() / this.config.weightFactorBatteryC(); + double min = Math.min(averageVoltageB, averageVoltageC); + weightB = (int) ((averageVoltageB - min) * factor); + weightC = (int) ((averageVoltageC - min) * factor); } } } - - // TODO discuss if this is correct! - int maxChargePower1 = 0; - int maxChargePower2 = 0; - int maxChargePower3 = 0; - int maxDischargePower1 = 0; - int maxDischargePower2 = 0; - int maxDischargePower3 = 0; - if (batteryStringA != null) { - maxChargePower1 = batteryStringA.getChargeMaxCurrent().value().asOptional().orElse(0) - * batteryStringA.getChargeMaxVoltage().value().asOptional().orElse(0); - maxDischargePower1 = batteryStringA.getDischargeMaxCurrent().value().asOptional().orElse(0) - * batteryStringA.getDischargeMinVoltage().value().asOptional().orElse(0); - } - - if (batteryStringB != null) { - maxChargePower2 = batteryStringB.getChargeMaxCurrent().value().asOptional().orElse(0) - * batteryStringB.getChargeMaxVoltage().value().asOptional().orElse(0); - maxDischargePower2 = batteryStringB.getDischargeMaxCurrent().value().asOptional().orElse(0) - * batteryStringB.getDischargeMinVoltage().value().asOptional().orElse(0); - } - if (batteryStringC != null) { - maxChargePower3 = batteryStringC.getChargeMaxCurrent().value().asOptional().orElse(0) - * batteryStringC.getChargeMaxVoltage().value().asOptional().orElse(0); - - maxDischargePower3 = batteryStringC.getDischargeMaxCurrent().value().asOptional().orElse(0) - * batteryStringC.getDischargeMinVoltage().value().asOptional().orElse(0); - - } - - log.info("doStringweighting(); active Power: " + activePower + "; weightA: " + weightA + "; weightB: " + weightB - + "; weightC: " + weightC); - - writeIPUParameters(weightA, weightB, weightC, maxDischargePower1, maxDischargePower2, maxDischargePower3, - maxChargePower1, maxChargePower2, maxChargePower3, getWeightingMode()); - } - - /** Writes the given value into the channel */ - // TODO should throws OpenemsException - void writeValueToChannel(GridConChannelId channelId, Object value) { - try { - ((WriteChannel) this.channel(channelId)).setNextWriteValueFromObject(value); - } catch (OpenemsException e) { - e.printStackTrace(); - log.error("Problem occurred during writing '" + value + "' to channel " + channelId.name()); - } + + FloatWriteChannel weightAchannel = this.channel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_A); + weightAchannel.setNextWriteValue(Float.valueOf(weightA)); + FloatWriteChannel weightBchannel = this.channel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_B); + weightBchannel.setNextWriteValue(Float.valueOf(weightB)); + FloatWriteChannel weightCchannel = this.channel(GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_C); + weightCchannel.setNextWriteValue(Float.valueOf(weightC)); } @Override public int getPowerPrecision() { - return 100; - } - - @Override - public void handleEvent(Event event) { - if (!this.isEnabled()) { - return; - } - switch (event.getTopic()) { - case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: - try { - this.evaluateGridMode(); - this.handleBatteryData(); - this.handleStateMachine(); - this.calculateSoC(); - - this.channel(GridConChannelId.STATE_CYCLE_ERROR).setNextValue(false); - } catch (IllegalArgumentException | OpenemsNamedException e) { - this.channel(GridConChannelId.STATE_CYCLE_ERROR).setNextValue(true); - this.logError(this.log, "State-Cycle Error: " + e.getMessage()); - } - break; - } - } - - /** - * Evaluates the Grid-Mode and sets the GRID_MODE channel accordingly. - * - * @throws OpenemsNamedException - * @throws IllegalArgumentException - */ - private void evaluateGridMode() throws IllegalArgumentException, OpenemsNamedException { - GridMode gridMode = GridMode.UNDEFINED; - try { - BooleanReadChannel inputNAProtection1 = this.componentManager - .getChannel(ChannelAddress.fromString(this.config.inputNAProtection1())); - BooleanReadChannel inputNAProtection2 = this.componentManager - .getChannel(ChannelAddress.fromString(this.config.inputNAProtection2())); - - Optional isInputNAProtection1 = inputNAProtection1.value().asOptional(); - Optional isInputNAProtection2 = inputNAProtection2.value().asOptional(); - - if (!isInputNAProtection1.isPresent() || !isInputNAProtection2.isPresent()) { - gridMode = GridMode.UNDEFINED; - } else { - if (isInputNAProtection1.get() && isInputNAProtection2.get()) { - gridMode = GridMode.ON_GRID; - } else { - gridMode = GridMode.OFF_GRID; - } - } - - } finally { - this.getGridMode().setNextValue(gridMode); - } - } - - /** - * Handles Battery data, i.e. setting allowed charge/discharge power. - */ - private void handleBatteryData() { - int allowedCharge = 0; - int allowedDischarge = 0; - for (Battery battery : this.getBatteries()) { - allowedCharge += battery.getVoltage().value().orElse(0) * battery.getChargeMaxCurrent().value().orElse(0) - * -1; - allowedDischarge += battery.getVoltage().value().orElse(0) - * battery.getDischargeMaxCurrent().value().orElse(0); - } - this.getAllowedCharge().setNextValue(allowedCharge); - this.getAllowedDischarge().setNextValue(allowedDischarge); - } - - private LocalDateTime offGridDetected = null; - int DO_NOTHING_IN_OFFGRID_FOR_THE_FIRST_SECONDS = 5; - - private void handleOffGridState() throws OpenemsNamedException { - - boolean disableIpu1 = false; - boolean disableIpu2 = true; - boolean disableIpu3 = true; - boolean disableIpu4 = true; - - switch (this.config.inverterCount()) { - case ONE: - disableIpu2 = false; - break; - case TWO: - disableIpu2 = false; - disableIpu3 = false; - break; - case THREE: - disableIpu2 = false; - disableIpu3 = false; - disableIpu4 = false; - break; - } - - // send play command -// commandControlWord.set(PCSControlWordBitPosition.PLAY.getBitPosition(), true); - - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1, disableIpu1); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, disableIpu2); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, disableIpu3); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, disableIpu4); - // Always set Voltage Control Mode + Blackstart Approval - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL, true); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL, false); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION, false); - this.setNextWriteValueToBooleanWriteChannel(GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING, false); - - // Always set OutputSyncDeviceBridge ON in Off-Grid state - log.info("Set K1 ON"); - this.setOutputSyncDeviceBridge(true); - // TODO check if OutputSyncDeviceBridge was actually set to ON via - // inputSyncDeviceBridgeComponent. On Error switch off the MR. - - if (offGridDetected == null) { - offGridDetected = LocalDateTime.now(); - return; - } - if (offGridDetected.plusSeconds(DO_NOTHING_IN_OFFGRID_FOR_THE_FIRST_SECONDS).isAfter(LocalDateTime.now())) { - return; - } - - // Measured by Grid-Meter, grid Values - SymmetricMeter gridMeter = this.componentManager.getComponent(this.config.meter()); - - int gridFreq = gridMeter.getFrequency().value().orElse(-1); - int gridVolt = gridMeter.getVoltage().value().orElse(-1); - - log.info("GridFreq: " + gridFreq + ", GridVolt: " + gridVolt); - - if (gridFreq == 0 || gridFreq < 49_700 || gridFreq > 50_300 || // - gridVolt == 0 || gridVolt < 215_000 || gridVolt > 245_000) { - log.info("Off-Grid -> F/U 1"); - /* - * Off-Grid - */ - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_U0, 1.0f); - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_F0, 1.0f); - - } else { - /* - * Going On-Grid - */ - int invSetFreq = gridFreq + 20; // add 20 mHz - int invSetVolt = gridVolt + 5_000; // add 5 V - float invSetFreqNormalized = invSetFreq / 50_000f; - float invSetVoltNormalized = invSetVolt / 230_000f; - log.info("Going On-Grid -> F/U " + invSetFreq + ", " + invSetVolt + ", " + invSetFreqNormalized + ", " - + invSetVoltNormalized); - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_U0, invSetVoltNormalized); - writeValueToChannel(GridConChannelId.COMMAND_CONTROL_PARAMETER_F0, invSetFreqNormalized); - } - } - - private void calculateSoC() { - double sumCapacity = 0; - double sumCurrentCapacity = 0; - for (Battery b : getBatteries()) { - sumCapacity = sumCapacity + b.getCapacity().value().asOptional().orElse(0); - sumCurrentCapacity = sumCurrentCapacity - + b.getCapacity().value().asOptional().orElse(0) * b.getSoc().value().orElse(0) / 100.0; - } - int soC = (int) (sumCurrentCapacity * 100 / sumCapacity); - this.getSoc().setNextValue(soC); + return 100; // TODO estimated value } /** @@ -1107,29 +529,42 @@ private Collection getBatteries() { return batteries; } + + public boolean isAtLeastOneBatteryReady() { + for (Battery battery : getBatteries()) { + Optional val = battery.getReadyForWorking().value().asOptional(); + + if (val.isPresent() && val.get()) { + return true; + } + } + return false; + } + @Override protected ModbusProtocol defineModbusProtocol() { int inverterCount = this.config.inverterCount().getCount(); + ModbusProtocol result = new ModbusProtocol(this, // /* * CCU State */ new FC3ReadRegistersTask(32528, Priority.HIGH, // - bm(new UnsignedWordElement(32528)) // - .m(GridConChannelId.CCU_STATE_IDLE, 0) // - .m(GridConChannelId.CCU_STATE_PRECHARGE, 1) // - .m(GridConChannelId.CCU_STATE_STOP_PRECHARGE, 2) // - .m(GridConChannelId.CCU_STATE_READY, 3) // - .m(GridConChannelId.CCU_STATE_PAUSE, 4) // - .m(GridConChannelId.CCU_STATE_RUN, 5) // - .m(GridConChannelId.CCU_STATE_ERROR, 6) // - .m(GridConChannelId.CCU_STATE_VOLTAGE_RAMPING_UP, 7) // - .m(GridConChannelId.CCU_STATE_OVERLOAD, 8) // - .m(GridConChannelId.CCU_STATE_SHORT_CIRCUIT_DETECTED, 9) // - .m(GridConChannelId.CCU_STATE_DERATING_POWER, 10) // - .m(GridConChannelId.CCU_STATE_DERATING_HARMONICS, 11) // - .m(GridConChannelId.CCU_STATE_SIA_ACTIVE, 12) // - .build(), // + m(new BitsWordElement(32528, this) // + .bit(0, GridConChannelId.CCU_STATE_IDLE) // + .bit(1, GridConChannelId.CCU_STATE_PRECHARGE) // + .bit(2, GridConChannelId.CCU_STATE_STOP_PRECHARGE) // + .bit(3, GridConChannelId.CCU_STATE_READY) // + .bit(4, GridConChannelId.CCU_STATE_PAUSE) // + .bit(5, GridConChannelId.CCU_STATE_RUN) // + .bit(6, GridConChannelId.CCU_STATE_ERROR) // + .bit(7, GridConChannelId.CCU_STATE_VOLTAGE_RAMPING_UP) // + .bit(8, GridConChannelId.CCU_STATE_OVERLOAD) // + .bit(9, GridConChannelId.CCU_STATE_SHORT_CIRCUIT_DETECTED) // + .bit(10, GridConChannelId.CCU_STATE_DERATING_POWER) // + .bit(11, GridConChannelId.CCU_STATE_DERATING_HARMONICS) // + .bit(12, GridConChannelId.CCU_STATE_SIA_ACTIVE) // + ), // new DummyRegisterElement(32529), m(GridConChannelId.CCU_ERROR_CODE, new UnsignedDoublewordElement(32530).wordOrder(WordOrder.LSWMSW)), // @@ -1153,28 +588,31 @@ protected ModbusProtocol defineModbusProtocol() { * Commands */ new FC16WriteRegistersTask(32560, // - bm(new UnsignedWordElement(32560)) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_PLAY, 0) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_READY, 1) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE, 2) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_STOP, 3) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL, 4) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL, 5) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING, 6) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION, 7) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_TRIGGER_SIA, 8) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION, 9) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET, 10) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET, 11) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET, 12) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET, 13) // - .build(), // - bm(new UnsignedWordElement(32561)) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, 12) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, 13) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, 14) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1, 15) // - .build(), // + m(new BitsWordElement(32560, this) // + .bit(0, GridConChannelId.COMMAND_CONTROL_WORD_PLAY) // + .bit(1, GridConChannelId.COMMAND_CONTROL_WORD_READY) // + .bit(2, GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE) // + .bit(3, GridConChannelId.COMMAND_CONTROL_WORD_STOP) // + + .bit(4, GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL) // + .bit(5, GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL) // + .bit(6, GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING) // + .bit(7, GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION) // + + .bit(8, GridConChannelId.COMMAND_CONTROL_WORD_TRIGGER_SIA) // + .bit(9, GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION) // + .bit(10, GridConChannelId.COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET) // + .bit(11, GridConChannelId.COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET) // + + .bit(12, GridConChannelId.COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET) // + .bit(13, GridConChannelId.COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET) // + ), // + m(new BitsWordElement(32561, this) // + .bit(12, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4) // + .bit(13, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3) // + .bit(14, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2) // + .bit(15, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1) // + ), // m(GridConChannelId.COMMAND_ERROR_CODE_FEEDBACK, new UnsignedDoublewordElement(32562).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.COMMAND_CONTROL_PARAMETER_U0, @@ -1189,33 +627,33 @@ protected ModbusProtocol defineModbusProtocol() { new UnsignedDoublewordElement(32572).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.COMMAND_TIME_SYNC_TIME, new UnsignedDoublewordElement(32574).wordOrder(WordOrder.LSWMSW)) // - ), + ).debug(), /* * Commands Mirror */ new FC3ReadRegistersTask(32880, Priority.LOW, // - bm(new UnsignedWordElement(32880)) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_PLAY, 0) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_READY, 1) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE, 2) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_STOP, 3) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL, 4) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL, 5) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING, 6) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION, 7) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_TRIGGER_SIA, 8) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION, 9) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET, 10) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET, 11) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET, 12) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET, 13) // - .build(), // - bm(new UnsignedWordElement(32881)) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, 12) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, 13) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, 14) // - .m(GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1, 15) // - .build(), // + m(new BitsWordElement(32880, this) // + .bit(12, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4) // + .bit(13, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3) // + .bit(14, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2) // + .bit(15, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1) // + ), // + m(new BitsWordElement(32881, this) // + .bit(0, GridConChannelId.COMMAND_CONTROL_WORD_PLAY) // + .bit(1, GridConChannelId.COMMAND_CONTROL_WORD_READY) // + .bit(2, GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE) // + .bit(3, GridConChannelId.COMMAND_CONTROL_WORD_STOP) // + .bit(4, GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL) // + .bit(5, GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL) // + .bit(6, GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING) // + .bit(7, GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION) // + .bit(8, GridConChannelId.COMMAND_CONTROL_WORD_TRIGGER_SIA) // + .bit(9, GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION) // + .bit(10, GridConChannelId.COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET) // + .bit(11, GridConChannelId.COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET) // + .bit(12, GridConChannelId.COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET) // + .bit(13, GridConChannelId.COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET) // + ), // m(GridConChannelId.COMMAND_ERROR_CODE_FEEDBACK, new UnsignedDoublewordElement(32882).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.COMMAND_CONTROL_PARAMETER_U0, @@ -1235,7 +673,7 @@ protected ModbusProtocol defineModbusProtocol() { new FloatDoublewordElement(32592).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.CONTROL_PARAMETER_U_Q_DROOP_T1_MAIN, new FloatDoublewordElement(32594).wordOrder(WordOrder.LSWMSW)), // - m(GridConChannelId.CONTROL_PARAMETER_F_P_DRROP_MAIN, + m(GridConChannelId.CONTROL_PARAMETER_F_P_DROOP_MAIN, new FloatDoublewordElement(32596).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.CONTROL_PARAMETER_F_P_DROOP_T1_MAIN, new FloatDoublewordElement(32598).wordOrder(WordOrder.LSWMSW)), // @@ -1263,7 +701,7 @@ protected ModbusProtocol defineModbusProtocol() { new FloatDoublewordElement(32620).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.CONTROL_PARAMETER_P_CONTROL_LIM_ONE, new FloatDoublewordElement(32622).wordOrder(WordOrder.LSWMSW)) // - ), + ).debug(), /* * Control Parameters Mirror */ @@ -1272,7 +710,7 @@ protected ModbusProtocol defineModbusProtocol() { new FloatDoublewordElement(32912).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.CONTROL_PARAMETER_U_Q_DROOP_T1_MAIN, new FloatDoublewordElement(32914).wordOrder(WordOrder.LSWMSW)), // - m(GridConChannelId.CONTROL_PARAMETER_F_P_DRROP_MAIN, + m(GridConChannelId.CONTROL_PARAMETER_F_P_DROOP_MAIN, new FloatDoublewordElement(32916).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.CONTROL_PARAMETER_F_P_DROOP_T1_MAIN, new FloatDoublewordElement(32918).wordOrder(WordOrder.LSWMSW)), // @@ -1353,7 +791,7 @@ protected ModbusProtocol defineModbusProtocol() { new FloatDoublewordElement(32636).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.INVERTER_1_CONTROL_P_MAX_CHARGE, new FloatDoublewordElement(32638).wordOrder(WordOrder.LSWMSW)) // - ), + ).debug(), /* * IPU 1 Mirror Control */ @@ -1436,7 +874,7 @@ protected ModbusProtocol defineModbusProtocol() { new FloatDoublewordElement(32668).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.INVERTER_2_CONTROL_P_MAX_CHARGE, new FloatDoublewordElement(32670).wordOrder(WordOrder.LSWMSW)) // - ), + ).debug(), /* * IPU 2 Mirror Control */ @@ -1457,7 +895,7 @@ protected ModbusProtocol defineModbusProtocol() { } if (inverterCount > 2) { /* - * At least 3 Inverters -> Add IPU 3 + * 3 Inverters -> Add IPU 3 */ result.addTasks(// /* @@ -1518,7 +956,7 @@ protected ModbusProtocol defineModbusProtocol() { new FloatDoublewordElement(32700).wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.INVERTER_3_CONTROL_P_MAX_CHARGE, new FloatDoublewordElement(32702).wordOrder(WordOrder.LSWMSW)) // - ), + ).debug(), /* * IPU 3 Mirror Control */ @@ -1589,7 +1027,7 @@ protected ModbusProtocol defineModbusProtocol() { .wordOrder(WordOrder.LSWMSW)), // m(GridConChannelId.DCDC_CONTROL_STRING_CONTROL_MODE, new FloatDoublewordElement(startAddressIpuControl + 14).wordOrder(WordOrder.LSWMSW)) // - ), + ).debug(), /* * DCDC Control Mirror */ @@ -1716,7 +1154,7 @@ protected ModbusProtocol defineModbusProtocol() { return result; } - private void setOutputSyncDeviceBridge(boolean value) throws IllegalArgumentException, OpenemsNamedException { + public void setOutputSyncDeviceBridge(boolean value) throws IllegalArgumentException, OpenemsNamedException { BooleanWriteChannel outputSyncDeviceBridge = this.componentManager .getChannel(ChannelAddress.fromString(this.config.outputSyncDeviceBridge())); this.setOutput(outputSyncDeviceBridge, value); @@ -1733,19 +1171,19 @@ private void setOutput(BooleanWriteChannel channel, boolean value) { log.info("Set output [" + channel.address() + "] " + (value ? "ON" : "OFF") + "."); try { channel.setNextWriteValue(value); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "Unable to set output: [" + channel.address() + "] " + e.getMessage()); } } } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - ManagedSymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(GridconPCS.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(GridconPCS.class, accessMode, 300) // .build()); } } diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/OffgridHandler.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/OffgridHandler.java new file mode 100644 index 00000000000..5bff1358ede --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/OffgridHandler.java @@ -0,0 +1,102 @@ +package io.openems.edge.ess.mr.gridcon; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.types.ChannelAddress; +import io.openems.edge.common.channel.BooleanReadChannel; +import io.openems.edge.ess.mr.gridcon.enums.InverterCount; +import io.openems.edge.ess.mr.gridcon.enums.PControlMode; +import io.openems.edge.ess.mr.gridcon.writeutils.CcuControlParameters; +import io.openems.edge.ess.mr.gridcon.writeutils.CommandControlRegisters; +import io.openems.edge.meter.api.SymmetricMeter; + +public class OffgridHandler { + + private final Logger log = LoggerFactory.getLogger(OffgridHandler.class); + private final StateMachine parent; + + public OffgridHandler(StateMachine parent) { + this.parent = parent; + } + + public void initialize() { + } + + protected StateMachine.State run() throws IllegalArgumentException, OpenemsNamedException { + System.out.println("OffgridHandler.run"); + // Are we still Off-Grid? + + BooleanReadChannel inputNAProtection1 = parent.parent.componentManager + .getChannel(ChannelAddress.fromString(parent.parent.config.inputNAProtection1())); + BooleanReadChannel inputNAProtection2 = parent.parent.componentManager + .getChannel(ChannelAddress.fromString(parent.parent.config.inputNAProtection2())); + + Optional isInputNAProtection1 = inputNAProtection1.value().asOptional(); + Optional isInputNAProtection2 = inputNAProtection2.value().asOptional(); + + if (isInputNAProtection1.isPresent() && isInputNAProtection1.get()) { + + if (isInputNAProtection2.isPresent() && isInputNAProtection2.get()) { + return StateMachine.State.ONGRID; + } else { + return StateMachine.State.GOING_ONGRID; + } + + } + + // Always set OutputSyncDeviceBridge ON in Off-Grid state + this.parent.parent.setOutputSyncDeviceBridge(true); + + // Measured by Grid-Meter, grid Values + SymmetricMeter gridMeter = this.parent.parent.componentManager.getComponent(this.parent.parent.config.meter()); + + Optional gridFreqOpt = gridMeter.getFrequency().value().asOptional(); + Optional gridVoltOpt = gridMeter.getVoltage().value().asOptional(); + + this.log.info("OffgridHandler.run() GridFreq: " + gridFreqOpt + ", GridVolt: " + gridVoltOpt); + + if (!gridFreqOpt.isPresent() || !gridVoltOpt.isPresent()) { + // TODO used to do doBlackStartGoingOnGrid() - correct? + return StateMachine.State.OFFGRID; + } + + int gridFreq = gridFreqOpt.get(); + int gridVolt = gridVoltOpt.get(); + + if (gridFreq == 0 || gridFreq < 49_700 || gridFreq > 50_300 || // + gridVolt == 0 || gridVolt < 215_000 || gridVolt > 245_000) { + /* + * Off-Grid + */ + log.info("OffgridHandler.doNormalBlackStartMode() Write channels for blackstart mode"); + InverterCount inverterCount = this.parent.parent.config.inverterCount(); + new CommandControlRegisters() // + .play(true) // + .syncApproval(false) // + .blackstartApproval(true) // + .modeSelection(CommandControlRegisters.Mode.VOLTAGE_CONTROL) // + .enableIpus(inverterCount) // + .parameterF0(GridconPCS.OFF_GRID_FREQUENCY_FACTOR) // + .parameterU0(GridconPCS.OFF_GRID_VOLTAGE_FACTOR) // + .writeToChannels(this.parent.parent); + new CcuControlParameters() // + .pControlMode(PControlMode.DISABLED) // + .qLimit(1f) // + .writeToChannels(this.parent.parent); + this.parent.parent.setIpuControlSettings(); + + } else { + /* + * Going On-Grid + * done in GoingOnGridHandler + */ + + } + + return StateMachine.State.OFFGRID; + } +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/OngridHandler.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/OngridHandler.java new file mode 100644 index 00000000000..9d8cd584f75 --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/OngridHandler.java @@ -0,0 +1,190 @@ +package io.openems.edge.ess.mr.gridcon; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.types.OptionsEnum; +import io.openems.edge.common.sum.GridMode; +import io.openems.edge.ess.mr.gridcon.enums.CCUState; +import io.openems.edge.ess.mr.gridcon.enums.InverterCount; +import io.openems.edge.ess.mr.gridcon.enums.PControlMode; +import io.openems.edge.ess.mr.gridcon.writeutils.CcuControlParameters; +import io.openems.edge.ess.mr.gridcon.writeutils.CommandControlRegisters; + +public class OngridHandler { + + private final Logger log = LoggerFactory.getLogger(OngridHandler.class); + private final StateMachine parent; + + private State state = State.UNDEFINED; + + public OngridHandler(StateMachine parent) { + this.parent = parent; + } + + public void initialize() { + this.state = State.UNDEFINED; + } + + protected StateMachine.State run() throws IllegalArgumentException, OpenemsNamedException { + System.out.println("OngridHandler.run"); + // Verify that we are still On-Grid -> otherwise switch to "Going Off-Grid" + GridMode gridMode = this.parent.parent.getGridMode().getNextValue().asEnum(); + switch (gridMode) { + case ON_GRID: + case UNDEFINED: + break; + case OFF_GRID: + return StateMachine.State.GOING_OFFGRID; + } + + // Always set OutputSyncDeviceBridge OFF in On-Grid state + this.parent.parent.setOutputSyncDeviceBridge(false); + + switch (this.state) { + case UNDEFINED: + this.state = this.doUndefined(); + break; + + case IDLE: + this.state = this.doIdle(); + break; + + case RUN: + this.state = this.doRun(); + break; + } + + return StateMachine.State.ONGRID; + } + + /** + * @return the next state + */ + private State doUndefined() { + CCUState ccuState = this.parent.getCcuState(); + switch (ccuState) { + case RUN: + return State.RUN; + + case IDLE: + return State.IDLE; + + case ERROR: + case DERATING_HARMONICS: + case DERATING_POWER: + case OVERLOAD: + case PAUSE: + case PRECHARGE: + case READY: + case SHORT_CIRCUIT_DETECTED: + case SIA_ACTIVE: + case STOP_PRECHARGE: + case UNDEFINED: + case VOLTAGE_RAMPING_UP: + this.log.info("Unhandled On-Grid state [" + ccuState.toString() + "]"); + break; + } + return State.UNDEFINED; + } + + /** + * Handles idle operation in On-Grid -> tries to start the inverter. + * + * @return the next state + * @throws OpenemsNamedException + * @throws IllegalArgumentException + */ + private State doIdle() throws IllegalArgumentException, OpenemsNamedException { + // Verify State + CCUState ccuState = this.parent.getCcuState(); + if (ccuState != CCUState.IDLE) { + return State.UNDEFINED; + } + + // If no battery is ready inverter cannot start + if (!this.parent.parent.isAtLeastOneBatteryReady()) { + return State.IDLE; + } + + InverterCount inverterCount = this.parent.parent.config.inverterCount(); + new CommandControlRegisters() // + .play(true) // Start system + .syncApproval(true) // + .blackstartApproval(false).shortCircuitHandling(true) // + .modeSelection(CommandControlRegisters.Mode.CURRENT_CONTROL) // + .parameterSet1(true) // + .parameterU0(GridconPCS.ON_GRID_VOLTAGE_FACTOR) // + .parameterF0(GridconPCS.ON_GRID_FREQUENCY_FACTOR) // + .enableIpus(inverterCount) // + .writeToChannels(this.parent.parent); + new CcuControlParameters() // + .pControlMode(PControlMode.ACTIVE_POWER_CONTROL) // + .qLimit(1f) // + .writeToChannels(this.parent.parent); + this.parent.parent.setIpuControlSettings(); + + return State.IDLE; + } + + private State doRun() throws IllegalArgumentException, OpenemsNamedException { + // Verify State + CCUState ccuState = this.parent.getCcuState(); + if (ccuState != CCUState.RUN) { + return State.UNDEFINED; + } + + InverterCount inverterCount = this.parent.parent.config.inverterCount(); + new CommandControlRegisters() // + .syncApproval(true) // + .blackstartApproval(false) // + .shortCircuitHandling(true) // + .modeSelection(CommandControlRegisters.Mode.CURRENT_CONTROL) // + .parameterSet1(true) // + .parameterU0(GridconPCS.ON_GRID_VOLTAGE_FACTOR) // + .parameterF0(GridconPCS.ON_GRID_FREQUENCY_FACTOR) // + .enableIpus(inverterCount) // + .writeToChannels(this.parent.parent); + new CcuControlParameters() // + .pControlMode(PControlMode.ACTIVE_POWER_CONTROL) // + .qLimit(1f) // + .writeToChannels(this.parent.parent); + this.parent.parent.setIpuControlSettings(); + + return State.RUN; + } + + public State getState() { + return state; + } + + public enum State implements OptionsEnum { + UNDEFINED(-1, "Undefined"), // + IDLE(1, "Idle"), // + RUN(2, "Run"); + + private final int value; + private final String name; + + private State(int value, String name) { + this.value = value; + this.name = name; + } + + @Override + public int getValue() { + return value; + } + + @Override + public String getName() { + return name; + } + + @Override + public OptionsEnum getUndefined() { + return UNDEFINED; + } + } +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/StateMachine.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/StateMachine.java new file mode 100644 index 00000000000..5fe618e1abf --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/StateMachine.java @@ -0,0 +1,288 @@ +package io.openems.edge.ess.mr.gridcon; + +import java.time.LocalDateTime; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.types.OptionsEnum; +import io.openems.edge.common.channel.BooleanReadChannel; +import io.openems.edge.common.channel.FloatReadChannel; +import io.openems.edge.common.sum.GridMode; +import io.openems.edge.ess.mr.gridcon.enums.CCUState; +import io.openems.edge.ess.mr.gridcon.enums.GridConChannelId; + +public class StateMachine { + + private static final int TIME_TOLERANCE_LINK_VOLTAGE = 15; + + protected final GridconPCS parent; + + private LocalDateTime ccuStateIsRunningSince = null; + + private final Logger log = LoggerFactory.getLogger(StateMachine.class); + private final GoingOngridHandler goingOngridHandler = new GoingOngridHandler(this); + private final GoingOffgridHandler goingOffgridHandler = new GoingOffgridHandler(this); + private final OngridHandler ongridHandler = new OngridHandler(this); + private final OffgridHandler offgridHandler = new OffgridHandler(this); + private final ErrorHandler errorHandler = new ErrorHandler(this); + + private State state = State.UNDEFINED; + private CCUState lastCcuState = CCUState.UNDEFINED; + + public StateMachine(GridconPCS parent) { + this.parent = parent; + + /* + * Call back for ccu state when ccu state is set to run a time variable is set + * this is important for checking the link voltage because the link voltage is + * not present at start up + */ + BooleanReadChannel ccuStateRunChannel = this.parent.channel(GridConChannelId.CCU_STATE_RUN); + ccuStateRunChannel.onChange(v -> { + Optional val = v.asOptional(); + if (!val.isPresent()) { + return; + } + + if (this.ccuStateIsRunningSince == null && val.get()) { + this.ccuStateIsRunningSince = LocalDateTime.now(); + } + + if (this.ccuStateIsRunningSince != null && !val.get()) { + this.ccuStateIsRunningSince = null; // it is not running + } + }); + } + + public void run() throws IllegalArgumentException, OpenemsNamedException { + /* + * Check if we have an Error + */ + if (this.isError()) { + this.switchState(State.ERROR); + } + + /* + * Handle State-Machine + */ + State nextState = null; + switch (this.state) { + case UNDEFINED: + nextState = this.handleUndefined(); + break; + + case GOING_ONGRID: + nextState = this.goingOngridHandler.run(); + break; + + case ONGRID: + nextState = this.ongridHandler.run(); + break; + + case GOING_OFFGRID: + nextState = this.goingOffgridHandler.run(); + break; + + case OFFGRID: + nextState = this.offgridHandler.run(); + break; + + case ERROR: + nextState = this.errorHandler.run(); + break; + } + if (nextState != this.state) { + this.switchState(nextState); + } + } + + /** + * Evaluates the current State. + * + * @return + * + * @throws OpenemsNamedException + * @throws IllegalArgumentException + */ + private State handleUndefined() { + GridMode gridMode = this.parent.getGridMode().getNextValue().asEnum(); + CCUState ccuState = this.getCcuState(); + if (ccuState == CCUState.ERROR) { + return State.ERROR; + } + + switch (gridMode) { + case ON_GRID: + return State.ONGRID; + + case OFF_GRID: + return State.OFFGRID; + + case UNDEFINED: + this.log.info("StateMachine.handleUndefined() -> staying UNDEFINED, Grid-Mode is [" + gridMode + "]"); + return State.UNDEFINED; + } + // should never come here + assert (true); + return State.UNDEFINED; + } + + private boolean isError() { + boolean result = false; + CCUState ccuState = this.getCcuState(); + // CCU State Error + if (this.lastCcuState != ccuState && ccuState == CCUState.ERROR) { + result = true; + } + this.lastCcuState = ccuState; + + // Link Voltage is too low + // TODO check Link-Voltage here? + + if (ccuState == CCUState.RUN && this.isLinkVoltageTooLow()) { + result = true; + } + + return result; + } + + protected boolean isLinkVoltageTooLow() { + + if (ccuStateIsRunningSince == null) { + return false; // if system is not running, validation is not possible + } + + if (ccuStateIsRunningSince.plusSeconds(TIME_TOLERANCE_LINK_VOLTAGE).isAfter(LocalDateTime.now())) { + return false; // system has to run a certain until validation is senseful + } + + FloatReadChannel frc = this.parent.channel(GridConChannelId.DCDC_STATUS_DC_LINK_POSITIVE_VOLTAGE); + Optional linkVoltageOpt = frc.value().asOptional(); + if (!linkVoltageOpt.isPresent()) { + return false; + } + + float linkVoltage = linkVoltageOpt.get(); + float difference = Math.abs(GridconPCS.DC_LINK_VOLTAGE_SETPOINT - linkVoltage); + + return (difference > GridconPCS.DC_LINK_VOLTAGE_TOLERANCE_VOLT); + } + + /** + * Gets the CCUState of the MR internal State-Machine. + * + * @return the CCUState + */ + protected CCUState getCcuState() { + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_ERROR)).value().asOptional() + .orElse(false)) { + return CCUState.ERROR; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_IDLE)).value().asOptional() + .orElse(false)) { + return CCUState.IDLE; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_PRECHARGE)).value().asOptional() + .orElse(false)) { + return CCUState.PRECHARGE; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_STOP_PRECHARGE)).value().asOptional() + .orElse(false)) { + return CCUState.STOP_PRECHARGE; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_READY)).value().asOptional() + .orElse(false)) { + return CCUState.READY; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_PAUSE)).value().asOptional() + .orElse(false)) { + return CCUState.PAUSE; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_RUN)).value().asOptional() + .orElse(false)) { + return CCUState.RUN; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_VOLTAGE_RAMPING_UP)).value() + .asOptional().orElse(false)) { + return CCUState.VOLTAGE_RAMPING_UP; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_OVERLOAD)).value().asOptional() + .orElse(false)) { + return CCUState.OVERLOAD; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_SHORT_CIRCUIT_DETECTED)).value() + .asOptional().orElse(false)) { + return CCUState.SHORT_CIRCUIT_DETECTED; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_DERATING_POWER)).value().asOptional() + .orElse(false)) { + return CCUState.DERATING_POWER; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_DERATING_HARMONICS)).value() + .asOptional().orElse(false)) { + return CCUState.DERATING_HARMONICS; + } + if (((BooleanReadChannel) this.parent.channel(GridConChannelId.CCU_STATE_SIA_ACTIVE)).value().asOptional() + .orElse(false)) { + return CCUState.SIA_ACTIVE; + } + return CCUState.UNDEFINED; + } + + /** + * Switches to the next state. + * + * @param nextState + */ + private void switchState(State nextState) { + // initialize all Handlers +// this.errorHandler.initialize(); //if error handler is always initialized newly, it has always state undef + + // TODO check set state to next state? + this.state = nextState; + } + + public State getState() { + return state; + } + + public OngridHandler getOngridHandler() { + return ongridHandler; + } + + public enum State implements OptionsEnum { + UNDEFINED(-1, "Undefined"), // + GOING_ONGRID(1, "Going On-Grid"), // + ONGRID(2, "On-Grid"), // + GOING_OFFGRID(3, "Going Off-Grid"), // + OFFGRID(4, "Off-Grid"), // + ERROR(5, "Error"); + + private final int value; + private final String name; + + private State(int value, String name) { + this.value = value; + this.name = name; + } + + @Override + public int getValue() { + return value; + } + + @Override + public String getName() { + return name; + } + + @Override + public OptionsEnum getUndefined() { + return UNDEFINED; + } + } + +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId0.java similarity index 99% rename from io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId.java rename to io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId0.java index f165ccc2040..0bca97af1fc 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId0.java @@ -1,15 +1,15 @@ package io.openems.edge.ess.mr.gridcon.enums; +import io.openems.common.channel.Level; import io.openems.edge.common.channel.ChannelId; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; import io.openems.edge.ess.mr.gridcon.enums.ErrorDoc.Acknowledge; import io.openems.edge.ess.mr.gridcon.enums.ErrorDoc.ReactionLevel; /** * This enum holds every possible error channel id for a gridcon. */ -public enum ErrorCodeChannelId implements ChannelId { +public enum ErrorCodeChannelId0 implements ChannelId { STATE_TEMP_TRIP_IGBT_3_IPU_1_1(new ErrorDoc(Level.WARNING) // .acknowledge(Acknowledge.RESTART) // @@ -5677,7 +5677,7 @@ public enum ErrorCodeChannelId implements ChannelId { private final Doc doc; - private ErrorCodeChannelId(Doc doc) { + private ErrorCodeChannelId0(Doc doc) { this.doc = doc; } diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId1.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId1.java index 355ec5059df..eceb2b01064 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId1.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorCodeChannelId1.java @@ -1,8 +1,8 @@ package io.openems.edge.ess.mr.gridcon.enums; +import io.openems.common.channel.Level; import io.openems.edge.common.channel.ChannelId; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; import io.openems.edge.ess.mr.gridcon.enums.ErrorDoc.Acknowledge; import io.openems.edge.ess.mr.gridcon.enums.ErrorDoc.ReactionLevel; @@ -73,7 +73,7 @@ public enum ErrorCodeChannelId1 implements ChannelId { STATE_INPUT_SLOT_Blackfin_1(new ErrorDoc(Level.WARNING) // .acknowledge(Acknowledge.UNDEFINED) // .reactionLevel(ReactionLevel.SHUTDOWN) // - .needsHardReset(false) // + .needsHardReset(true) // after this error it was even not possible to acknowledge it with MR-Tool, so a hard reset has been necessary .code(0x06000A) // .text("InputSlot Timeout")), STATE_COM_SLOT_Sharc_1(new ErrorDoc(Level.WARNING) // diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorDoc.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorDoc.java index 4383cb54028..e178d34beb3 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorDoc.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/ErrorDoc.java @@ -3,7 +3,7 @@ import java.util.HashMap; import java.util.Map; -import io.openems.edge.common.channel.Level; +import io.openems.common.channel.Level; import io.openems.edge.common.channel.StateChannelDoc; public class ErrorDoc extends StateChannelDoc { diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/GridConChannelId.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/GridConChannelId.java index 1ed07d4c4b7..4c039caa4b3 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/GridConChannelId.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/GridConChannelId.java @@ -1,7 +1,9 @@ package io.openems.edge.ess.mr.gridcon.enums; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.BooleanWriteChannel; import io.openems.edge.common.channel.ChannelId; @@ -10,8 +12,6 @@ import io.openems.edge.common.channel.FloatWriteChannel; import io.openems.edge.common.channel.IntegerDoc; import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; /** * This enum holds every possible channel id for a gridcon. @@ -137,81 +137,94 @@ public enum GridConChannelId implements ChannelId { DCDC_MEASUREMENTS_RESERVE_1(Doc.of(OpenemsType.FLOAT).unit(Unit.AMPERE)), // DCDC_MEASUREMENTS_RESERVE_2(Doc.of(OpenemsType.FLOAT).unit(Unit.PERCENT)), - COMMAND_CONTROL_WORD_PLAY_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_PLAY(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_PLAY_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_PLAY(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel(GridConChannelId.COMMAND_CONTROL_WORD_PLAY_DEBUG))), - - COMMAND_CONTROL_WORD_READY_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_READY(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_READY_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_READY(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel(GridConChannelId.COMMAND_CONTROL_WORD_READY_DEBUG))), - - COMMAND_CONTROL_WORD_ACKNOWLEDGE_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_ACKNOWLEDGE(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_ACKNOWLEDGE_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_ACKNOWLEDGE(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE_DEBUG))), - - COMMAND_CONTROL_WORD_STOP_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_STOP(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_STOP_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_STOP(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel(GridConChannelId.COMMAND_CONTROL_WORD_STOP_DEBUG))), - - COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL_DEBUG))), - - COMMAND_CONTROL_WORD_SYNC_APPROVAL_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_SYNC_APPROVAL(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_SYNC_APPROVAL_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_SYNC_APPROVAL(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL_DEBUG))), - - COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING( - new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel( - GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING_DEBUG))), - - COMMAND_CONTROL_WORD_MODE_SELECTION_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_MODE_SELECTION(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel( + GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING_DEBUG))), + COMMAND_CONTROL_WORD_MODE_SELECTION_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_MODE_SELECTION(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION_DEBUG))), - - COMMAND_CONTROL_WORD_TRIGGER_SIA_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_TRIGGER_SIA(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_TRIGGER_SIA_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_TRIGGER_SIA(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_TRIGGER_SIA_DEBUG))), - - COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION( - new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel( - GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION_DEBUG))), - - COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel( + GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION_DEBUG))), + COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET_DEBUG))), - - COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET_DEBUG))), - - COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET_DEBUG))), - - COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET(new BooleanDoc().unit(Unit.ON_OFF).accessMode(AccessMode.READ_WRITE) // + COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET_DEBUG))), - - COMMAND_CONTROL_WORD_DISABLE_IPU_4(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), - COMMAND_CONTROL_WORD_DISABLE_IPU_3(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), - COMMAND_CONTROL_WORD_DISABLE_IPU_2(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), - COMMAND_CONTROL_WORD_DISABLE_IPU_1(Doc.of(OpenemsType.FLOAT).unit(Unit.ON_OFF)), // - - COMMAND_ERROR_CODE_FEEDBACK_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // + COMMAND_CONTROL_WORD_DISABLE_IPU_4_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_DISABLE_IPU_4(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel( + GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4_DEBUG))), + COMMAND_CONTROL_WORD_DISABLE_IPU_3_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_DISABLE_IPU_3(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel( + GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3_DEBUG))), + COMMAND_CONTROL_WORD_DISABLE_IPU_2_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_DISABLE_IPU_2(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel( + GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2_DEBUG))), + COMMAND_CONTROL_WORD_DISABLE_IPU_1_DEBUG(Doc.of(OpenemsType.BOOLEAN)), // + COMMAND_CONTROL_WORD_DISABLE_IPU_1(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel( + GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1_DEBUG))), + COMMAND_ERROR_CODE_FEEDBACK_DEBUG(Doc.of(OpenemsType.INTEGER).unit(Unit.NONE)), // COMMAND_ERROR_CODE_FEEDBACK(new IntegerDoc() // .accessMode(AccessMode.READ_WRITE) .onInit(new IntegerWriteChannel.MirrorToDebugChannel(GridConChannelId.COMMAND_ERROR_CODE_FEEDBACK_DEBUG))), @@ -252,12 +265,12 @@ public enum GridConChannelId implements ChannelId { .onInit(new FloatWriteChannel.MirrorToDebugChannel( GridConChannelId.COMMAND_CONTROL_PARAMETER_P_REF_DEBUG))), - COMMAND_TIME_SYNC_DATE_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // + COMMAND_TIME_SYNC_DATE_DEBUG(Doc.of(OpenemsType.INTEGER).unit(Unit.NONE)), // COMMAND_TIME_SYNC_DATE(new IntegerDoc() // .accessMode(AccessMode.READ_WRITE) .onInit(new IntegerWriteChannel.MirrorToDebugChannel(GridConChannelId.COMMAND_TIME_SYNC_DATE_DEBUG))), - COMMAND_TIME_SYNC_TIME_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // + COMMAND_TIME_SYNC_TIME_DEBUG(Doc.of(OpenemsType.INTEGER).unit(Unit.NONE)), // COMMAND_TIME_SYNC_TIME(new IntegerDoc() // .accessMode(AccessMode.READ_WRITE) .onInit(new IntegerWriteChannel.MirrorToDebugChannel(GridConChannelId.COMMAND_TIME_SYNC_TIME_DEBUG))), @@ -275,7 +288,7 @@ public enum GridConChannelId implements ChannelId { GridConChannelId.CONTROL_PARAMETER_U_Q_DROOP_T1_MAIN_DEBUG))), CONTROL_PARAMETER_F_P_DRROP_MAIN_DEBUG(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), - CONTROL_PARAMETER_F_P_DRROP_MAIN(new FloatDoc() // + CONTROL_PARAMETER_F_P_DROOP_MAIN(new FloatDoc() // .accessMode(AccessMode.READ_WRITE) .onInit(new FloatWriteChannel.MirrorToDebugChannel( GridConChannelId.CONTROL_PARAMETER_F_P_DRROP_MAIN_DEBUG))), @@ -500,14 +513,14 @@ public enum GridConChannelId implements ChannelId { .onInit(new FloatWriteChannel.MirrorToDebugChannel( GridConChannelId.INVERTER_3_CONTROL_P_MAX_CHARGE_DEBUG))), - DCDC_CONTROL_DC_VOLTAGE_SETPOINT(Doc.of(OpenemsType.FLOAT).unit(Unit.VOLT)), - DCDC_CONTROL_WEIGHT_STRING_A(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // - DCDC_CONTROL_WEIGHT_STRING_B(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // - DCDC_CONTROL_WEIGHT_STRING_C(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // - DCDC_CONTROL_I_REF_STRING_A(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // - DCDC_CONTROL_I_REF_STRING_B(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // - DCDC_CONTROL_I_REF_STRING_C(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), // - DCDC_CONTROL_STRING_CONTROL_MODE(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE)), + DCDC_CONTROL_DC_VOLTAGE_SETPOINT(Doc.of(OpenemsType.FLOAT).unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), + DCDC_CONTROL_WEIGHT_STRING_A(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DCDC_CONTROL_WEIGHT_STRING_B(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DCDC_CONTROL_WEIGHT_STRING_C(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DCDC_CONTROL_I_REF_STRING_A(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DCDC_CONTROL_I_REF_STRING_B(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE).accessMode(AccessMode.READ_WRITE).accessMode(AccessMode.READ_WRITE)), // + DCDC_CONTROL_I_REF_STRING_C(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DCDC_CONTROL_STRING_CONTROL_MODE(Doc.of(OpenemsType.FLOAT).unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), STATE_CYCLE_ERROR(Doc.of(Level.FAULT)); diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/InverterCount.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/InverterCount.java index 9b246ccaf47..391591ac371 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/InverterCount.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/InverterCount.java @@ -1,17 +1,25 @@ package io.openems.edge.ess.mr.gridcon.enums; +import io.openems.edge.ess.mr.gridcon.GridconPCS; + public enum InverterCount { - ONE(1), - TWO(2), + ONE(1), // + TWO(2), // THREE(3); - - private int count; - + + private final int count; + private final int maxApparentPower; + private InverterCount(int count) { this.count = count; + this.maxApparentPower = count * GridconPCS.MAX_POWER_PER_INVERTER; } - + public int getCount() { return this.count; } + + public int getMaxApparentPower() { + return maxApparentPower; + } } diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/PControlMode.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/PControlMode.java index 815bd8e2a80..8e808a2eae0 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/PControlMode.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/PControlMode.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.mr.gridcon.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PControlMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStateMachine.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStateMachine.java index 2cd656d5c55..98e654005d2 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStateMachine.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStateMachine.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.mr.gridcon.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum StatusIPUStateMachine implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStatusMCU.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStatusMCU.java index 6ab29639256..1ae9353f9f5 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStatusMCU.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/enums/StatusIPUStatusMCU.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.mr.gridcon.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum StatusIPUStatusMCU implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/CcuControlParameters.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/CcuControlParameters.java new file mode 100644 index 00000000000..c4caa547643 --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/CcuControlParameters.java @@ -0,0 +1,74 @@ +package io.openems.edge.ess.mr.gridcon.writeutils; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.common.channel.WriteChannel; +import io.openems.edge.ess.mr.gridcon.GridconPCS; +import io.openems.edge.ess.mr.gridcon.enums.GridConChannelId; +import io.openems.edge.ess.mr.gridcon.enums.PControlMode; + +public class CcuControlParameters { + + // 32592 + private float uByQDroopMain = 0f; + private float uByQDroopT1Main = 0f; + private float fByPDroopMain = 0f; + private float fByPDroopT1Main = 0f; + private float qByUDroopMain = 0f; + private float qByUDeadBand = 0f; + private float qLimit = 0f; + private float pByFDroopMain = 0f; + private float pByFDeadBand = 0f; + private float pByUDroop = 0f; + private float pByUDeadBand = 0f; + private float pByUMaxCharge = 0f; + private float pByUMaxDischarge = 0f; + private PControlMode pControlMode = PControlMode.ACTIVE_POWER_CONTROL; + private float pControlLimTwo = 0f; + private float pControlLimOne = 0f; + + public CcuControlParameters pControlMode(PControlMode value) { + this.pControlMode = value; + return this; + } + + /** + * 0 -> limits Q to zero, 1 -> to max Q. + * + * @param value + * @return + * @throws OpenemsException + */ + public CcuControlParameters qLimit(float value) throws OpenemsException { + if (value < 0 || value > 1) { + throw new OpenemsException("Q-Limit needs to be within 0 and 1."); + } + this.qLimit = value; + return this; + } + + public void writeToChannels(GridconPCS parent) throws IllegalArgumentException, OpenemsNamedException { + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_U_Q_DROOP_MAIN, this.uByQDroopMain); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_U_Q_DROOP_T1_MAIN, this.uByQDroopT1Main); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_F_P_DROOP_MAIN, this.fByPDroopMain); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_F_P_DROOP_T1_MAIN, this.fByPDroopT1Main); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_Q_U_DROOP_MAIN, this.qByUDroopMain); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_Q_U_DEAD_BAND, this.qByUDeadBand); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_Q_LIMIT, this.qLimit); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_F_DROOP_MAIN, this.pByFDroopMain); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_F_DEAD_BAND, this.pByFDeadBand); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_U_DROOP, this.pByUDroop); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_U_DEAD_BAND, this.pByUDeadBand); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_U_MAX_CHARGE, this.pByUMaxCharge); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_U_MAX_DISCHARGE, this.pByUMaxDischarge); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_CONTROL_MODE, + this.pControlMode.getFloatValue()); // + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_CONTROL_LIM_TWO, this.pControlLimTwo); + this.writeValueToChannel(parent, GridConChannelId.CONTROL_PARAMETER_P_CONTROL_LIM_ONE, this.pControlLimOne); + } + + private void writeValueToChannel(GridconPCS parent, GridConChannelId channelId, T value) + throws IllegalArgumentException, OpenemsNamedException { + ((WriteChannel) parent.channel(channelId)).setNextWriteValueFromObject(value); + } +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/CommandControlRegisters.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/CommandControlRegisters.java new file mode 100644 index 00000000000..4ff6e4df110 --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/CommandControlRegisters.java @@ -0,0 +1,251 @@ +package io.openems.edge.ess.mr.gridcon.writeutils; + +import java.time.LocalDateTime; +import java.util.BitSet; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.common.channel.WriteChannel; +import io.openems.edge.ess.mr.gridcon.GridconPCS; +import io.openems.edge.ess.mr.gridcon.enums.GridConChannelId; +import io.openems.edge.ess.mr.gridcon.enums.InverterCount; + +public class CommandControlRegisters { + + public static enum Mode { + CURRENT_CONTROL(true), // + VOLTAGE_CONTROL(false); + + private final boolean value; + + private Mode(boolean value) { + this.value = value; + } + } + + // 32560 + private boolean disableIpu1 = true; + private boolean disableIpu2 = true; + private boolean disableIpu3 = true; + private boolean disableIpu4 = true; + + // 32561 + private boolean play = false; + private boolean ready = false; + private boolean acknowledge = false; + private boolean stop = false; + private boolean blackstartApproval = false; + private boolean syncApproval = false; + private boolean shortCircuitHandling = false; + private Mode modeSelection = Mode.VOLTAGE_CONTROL; + private boolean triggerSia = false; + private boolean harmonicCompensation = false; + private boolean parameterSet1 = false; + private boolean parameterSet2 = false; + private boolean parameterSet3 = false; + private boolean parameterSet4 = false; + + // 32562 + private int errorCodeFeedback = 0; + private float parameterU0 = 0f; + private float parameterF0 = 0f; + private final float parameterQref = 0f; // is set in applyPower() + private final float parameterPref = 0f; // is set in applyPower() + + public CommandControlRegisters play(boolean value) { + this.play = value; + return this; + } + + public CommandControlRegisters ready(boolean value) { + this.ready = value; + return this; + } + + public CommandControlRegisters acknowledge(boolean value) { + this.acknowledge = value; + return this; + } + + public CommandControlRegisters stop(boolean value) { + this.stop = value; + return this; + } + + public CommandControlRegisters blackstartApproval(boolean value) { + this.blackstartApproval = value; + return this; + } + + public CommandControlRegisters syncApproval(boolean value) { + this.syncApproval = value; + return this; + } + + public CommandControlRegisters shortCircuitHandling(boolean value) { + this.shortCircuitHandling = value; + return this; + } + + public CommandControlRegisters modeSelection(Mode value) { + this.modeSelection = value; + return this; + } + + public CommandControlRegisters triggerSia(boolean value) { + this.triggerSia = value; + return this; + } + + public CommandControlRegisters harmonicCompensation(boolean value) { + this.harmonicCompensation = value; + return this; + } + + public CommandControlRegisters parameterSet1(boolean value) { + this.parameterSet1 = value; + return this; + } + + public CommandControlRegisters parameterSet2(boolean value) { + this.parameterSet2 = value; + return this; + } + + public CommandControlRegisters parameterSet3(boolean value) { + this.parameterSet3 = value; + return this; + } + + public CommandControlRegisters parameterSet4(boolean value) { + this.parameterSet4 = value; + return this; + } + + public CommandControlRegisters enableIpus(InverterCount inverterCount) { + switch (inverterCount) { + case ONE: + this.disableIpu1 = false; + this.disableIpu2 = false; + this.disableIpu3 = true; + this.disableIpu4 = true; + break; + case TWO: + this.disableIpu1 = false; + this.disableIpu2 = false; + this.disableIpu3 = false; + this.disableIpu4 = true; + break; + case THREE: + this.disableIpu1 = false; + this.disableIpu2 = false; + this.disableIpu3 = false; + this.disableIpu4 = false; + break; + } + return this; + } + + public CommandControlRegisters disableIpu2(boolean value) { + this.disableIpu2 = value; + return this; + } + + public CommandControlRegisters disableIpu3(boolean value) { + this.disableIpu3 = value; + return this; + } + + public CommandControlRegisters disableIpu4(boolean value) { + this.disableIpu4 = value; + return this; + } + + public CommandControlRegisters errorCodeFeedback(int value) { + this.errorCodeFeedback = value; + return this; + } + + public CommandControlRegisters parameterU0(float value) { + this.parameterU0 = value; + return this; + } + + public CommandControlRegisters parameterF0(float value) { + this.parameterF0 = value; + return this; + } + + public void writeToChannels(GridconPCS parent) throws IllegalArgumentException, OpenemsNamedException { + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_PLAY, this.play); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_READY, this.ready); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_ACKNOWLEDGE, this.acknowledge); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_STOP, this.stop); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_BLACKSTART_APPROVAL, + this.blackstartApproval); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_SYNC_APPROVAL, this.syncApproval); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_SHORT_CIRCUIT_HANDLING, + this.shortCircuitHandling); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_MODE_SELECTION, + this.modeSelection.value); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_TRIGGER_SIA, this.triggerSia); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_ACTIVATE_HARMONIC_COMPENSATION, + this.harmonicCompensation); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_ID_1_SD_CARD_PARAMETER_SET, + this.parameterSet1); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_ID_2_SD_CARD_PARAMETER_SET, + this.parameterSet2); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_ID_3_SD_CARD_PARAMETER_SET, + this.parameterSet3); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_ID_4_SD_CARD_PARAMETER_SET, + this.parameterSet4); + + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_1, this.disableIpu1); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_2, this.disableIpu2); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_3, this.disableIpu3); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_WORD_DISABLE_IPU_4, this.disableIpu4); + + this.writeValueToChannel(parent, GridConChannelId.COMMAND_ERROR_CODE_FEEDBACK, this.errorCodeFeedback); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_PARAMETER_U0, this.parameterU0); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_PARAMETER_F0, this.parameterF0); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_PARAMETER_Q_REF, this.parameterQref); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_CONTROL_PARAMETER_P_REF, this.parameterPref); + + int date = this.convertToInteger(this.generateDate()); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_TIME_SYNC_DATE, date); + int time = this.convertToInteger(this.generateTime()); + this.writeValueToChannel(parent, GridConChannelId.COMMAND_TIME_SYNC_TIME, time); + } + + private void writeValueToChannel(GridconPCS parent, GridConChannelId channelId, T value) + throws IllegalArgumentException, OpenemsNamedException { + ((WriteChannel) parent.channel(channelId)).setNextWriteValueFromObject(value); + } + + private BitSet generateDate() { + LocalDateTime time = LocalDateTime.now(); + byte dayOfWeek = (byte) time.getDayOfWeek().ordinal(); + byte day = (byte) time.getDayOfMonth(); + byte month = (byte) time.getMonth().getValue(); + byte year = (byte) (time.getYear() - 2000); // 0 == year 2000 in the protocol + + return BitSet.valueOf(new byte[] { day, dayOfWeek, year, month }); + } + + private BitSet generateTime() { + LocalDateTime time = LocalDateTime.now(); + byte seconds = (byte) time.getSecond(); + byte minutes = (byte) time.getMinute(); + byte hours = (byte) time.getHour(); + + // second byte is unused + return BitSet.valueOf(new byte[] { seconds, 0, hours, minutes }); + } + + private int convertToInteger(BitSet bitSet) { + long[] l = bitSet.toLongArray(); + if (l.length == 0) { + return 0; + } + return (int) l[0]; + } +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/DcdcControl.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/DcdcControl.java new file mode 100644 index 00000000000..31b3c8c7376 --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/DcdcControl.java @@ -0,0 +1,95 @@ +package io.openems.edge.ess.mr.gridcon.writeutils; + +import io.openems.common.exceptions.OpenemsException; + +import java.util.Optional; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.battery.api.Battery; +import io.openems.edge.common.channel.WriteChannel; +import io.openems.edge.common.component.ComponentManager; +import io.openems.edge.ess.mr.gridcon.Config; +import io.openems.edge.ess.mr.gridcon.GridconPCS; +import io.openems.edge.ess.mr.gridcon.enums.GridConChannelId; + +public class DcdcControl { + + private float dcVoltageSetpoint = 0f; + private final float weightStringA = 0f; // is set in applyPower() + private final float weightStringB = 0f; // is set in applyPower() + private final float weightStringC = 0f; // is set in applyPower() + private float iRefStringA = 0f; + private float iRefStringB = 0f; + private float iRefStringC = 0f; + private float stringControlMode = 0f; + + public DcdcControl dcVoltageSetpoint(float value) { + this.dcVoltageSetpoint = value; + return this; + } + + public DcdcControl iRefStringA(float value) { + this.iRefStringA = value; + return this; + } + + public DcdcControl iRefStringB(float value) { + this.iRefStringB = value; + return this; + } + + public DcdcControl iRefStringC(float value) { + this.iRefStringC = value; + return this; + } + + public DcdcControl stringControlMode(ComponentManager componentManager, Config config) + throws OpenemsNamedException { + int weightingMode = 0; // Depends on number of battery strings!!! + + weightingMode = weightingMode + calcWeightingMode(config.batteryStringA_id(), 1, componentManager); // battA = 1 (2^0) + weightingMode = weightingMode + calcWeightingMode(config.batteryStringB_id(), 8, componentManager); // battB = 8 (2^3) + weightingMode = weightingMode + calcWeightingMode(config.batteryStringC_id(), 64, componentManager); // battC = 64 (2^6) + + System.out.println("Weighting mode: " + weightingMode); + + this.stringControlMode = weightingMode; + + return this; + } + + private int calcWeightingMode(String batteryStringId, int weightingValue, ComponentManager componentManager) throws OpenemsNamedException { + //If the battery is connected and switched on and ready for working it can be considered for weighting + if (batteryStringId != null && batteryStringId.length() > 0) { + Battery battery = componentManager.getComponent(batteryStringId); + if (battery != null) { + Optional batteryReady = battery.getReadyForWorking().value().asOptional(); + if (batteryReady.isPresent() && batteryReady.get()) { + return weightingValue; + } + } + } + return 0; + } + + public void writeToChannels(GridconPCS parent) throws IllegalArgumentException, OpenemsNamedException { + // weighting is never allowed to be '0' + if (this.stringControlMode == 0) { + throw new OpenemsException("Calculated weight of '0' -> not allowed!"); + } + + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_DC_VOLTAGE_SETPOINT, this.dcVoltageSetpoint); + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_A, this.weightStringA); + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_B, this.weightStringB); + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_WEIGHT_STRING_C, this.weightStringC); + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_I_REF_STRING_A, this.iRefStringA); + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_I_REF_STRING_B, this.iRefStringB); + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_I_REF_STRING_C, this.iRefStringC); + this.writeValueToChannel(parent, GridConChannelId.DCDC_CONTROL_STRING_CONTROL_MODE, this.stringControlMode); + } + + private void writeValueToChannel(GridconPCS parent, GridConChannelId channelId, T value) + throws IllegalArgumentException, OpenemsNamedException { + ((WriteChannel) parent.channel(channelId)).setNextWriteValueFromObject(value); + } +} diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/IpuInverterControl.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/IpuInverterControl.java new file mode 100644 index 00000000000..378bc1349e6 --- /dev/null +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/writeutils/IpuInverterControl.java @@ -0,0 +1,97 @@ +package io.openems.edge.ess.mr.gridcon.writeutils; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.common.channel.WriteChannel; +import io.openems.edge.ess.mr.gridcon.GridconPCS; +import io.openems.edge.ess.mr.gridcon.enums.GridConChannelId; + +public class IpuInverterControl { + + public enum Inverter { + ONE, // + TWO, // + THREE; + } + + // 32592 + private float dcVoltageSetpoint = 0f; + private float dcCurrentSetpoint = 0f; + private float u0OffsetToCcu = 0f; + private float f0OffsetToCcu = 0f; + private float qRefOffsetToCcu = 0f; + private float pRefOffsetToCcu = 0f; + private float pMaxDischarge = 0f; + private float pMaxCharge = 0f; + + public IpuInverterControl pMaxDischarge(float value) { + this.pMaxDischarge = value; + return this; + } + + public IpuInverterControl pMaxCharge(float value) { + this.pMaxCharge = value; + return this; + } + + public IpuInverterControl writeToChannels(GridconPCS parent, Inverter inverter) + throws IllegalArgumentException, OpenemsNamedException { + switch (inverter) { + case ONE: + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_DC_VOLTAGE_SETPOINT, + this.dcVoltageSetpoint); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_DC_CURRENT_SETPOINT, + this.dcCurrentSetpoint); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_U0_OFFSET_TO_CCU_VALUE, + this.u0OffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_F0_OFFSET_TO_CCU_VALUE, + this.f0OffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, + this.qRefOffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, + this.pRefOffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_P_MAX_DISCHARGE, this.pMaxDischarge); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_1_CONTROL_P_MAX_CHARGE, this.pMaxCharge); + break; + + case TWO: + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_DC_VOLTAGE_SETPOINT, + this.dcVoltageSetpoint); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_DC_CURRENT_SETPOINT, + this.dcCurrentSetpoint); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_U0_OFFSET_TO_CCU_VALUE, + this.u0OffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_F0_OFFSET_TO_CCU_VALUE, + this.f0OffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, + this.qRefOffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, + this.pRefOffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_P_MAX_DISCHARGE, this.pMaxDischarge); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_2_CONTROL_P_MAX_CHARGE, this.pMaxCharge); + break; + + case THREE: + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_DC_VOLTAGE_SETPOINT, + this.dcVoltageSetpoint); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_DC_CURRENT_SETPOINT, + this.dcCurrentSetpoint); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_U0_OFFSET_TO_CCU_VALUE, + this.u0OffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_F0_OFFSET_TO_CCU_VALUE, + this.f0OffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_Q_REF_OFFSET_TO_CCU_VALUE, + this.qRefOffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_P_REF_OFFSET_TO_CCU_VALUE, + this.pRefOffsetToCcu); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_P_MAX_DISCHARGE, this.pMaxDischarge); + this.writeValueToChannel(parent, GridConChannelId.INVERTER_3_CONTROL_P_MAX_CHARGE, this.pMaxCharge); + break; + } + return this; + } + + private void writeValueToChannel(GridconPCS parent, GridConChannelId channelId, T value) + throws IllegalArgumentException, OpenemsNamedException { + ((WriteChannel) parent.channel(channelId)).setNextWriteValueFromObject(value); + } +} diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryMode.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryMode.java index 1ee5ce9740b..e388def32ee 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryMode.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryMode.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.refu; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BatteryMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryState.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryState.java index 0f816fd1d0b..918a1abda2c 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryState.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/BatteryState.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.refu; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BatteryState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/Config.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/Config.java index fe788648f51..b9fdac348e4 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/Config.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/Config.java @@ -7,8 +7,14 @@ name = "ESS Refu", // description = "The energy storage system implementation of a Refu Ess.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; - + + @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/DcdcStatus.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/DcdcStatus.java index c587cef8542..9d9d234ec01 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/DcdcStatus.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/DcdcStatus.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.refu; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum DcdcStatus implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/ErrorHandler.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/ErrorHandler.java index 1a7c9aef614..97bfd9f3b48 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/ErrorHandler.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/ErrorHandler.java @@ -5,7 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.channel.EnumWriteChannel; import io.openems.edge.common.channel.IntegerReadChannel; import io.openems.edge.ess.refu.RefuEss.ChannelId; @@ -67,7 +67,7 @@ public void run() { case PRE_OPERATION: try { setWorkStateChannel.setNextWriteValue(StopStart.START.getValue()); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to Set Work-State to START"); } break; @@ -104,7 +104,7 @@ public void run() { } try { setWorkStateChannel.setNextWriteValue(StopStart.STOP.getValue()); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to Set Work-State to STOP"); } if (this.lastErrorReset.isAfter(LocalDateTime.now().minusHours(2))) { @@ -129,7 +129,7 @@ public void run() { } else { try { systemErrorResetChannel.setNextWriteValue(StopStart.START.getValue()); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to Set System-Error-Reset to START"); } } @@ -142,7 +142,7 @@ public void run() { } else { try { systemErrorResetChannel.setNextWriteValue(StopStart.STOP.getValue()); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to Set System-Error-Reset to STOP"); } } diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/RefuEss.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/RefuEss.java index ea680af02a9..d2276cab3c0 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/RefuEss.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/RefuEss.java @@ -16,11 +16,16 @@ import org.osgi.service.metatype.annotations.Designate; import org.slf4j.Logger; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.types.OpenemsType; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; import io.openems.edge.bridge.modbus.api.element.SignedDoublewordElement; import io.openems.edge.bridge.modbus.api.element.SignedWordElement; @@ -31,9 +36,6 @@ import io.openems.edge.bridge.modbus.api.task.FC4ReadInputRegistersTask; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.AccessMode; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.common.modbusslave.ModbusSlave; @@ -50,7 +52,6 @@ import io.openems.edge.ess.power.api.Power; import io.openems.edge.ess.power.api.Pwr; import io.openems.edge.ess.power.api.Relationship; -import io.openems.common.types.OpenemsType; @Designate(ocd = Config.class, factory = true) @Component( // @@ -89,7 +90,7 @@ public RefuEss() { @Override public void applyPower(int activePowerL1, int reactivePowerL1, int activePowerL2, int reactivePowerL2, - int activePowerL3, int reactivePowerL3) throws OpenemsException { + int activePowerL3, int reactivePowerL3) throws OpenemsNamedException { int activePower = activePowerL1 + activePowerL2 + activePowerL3; int allowedCharge = this.getAllowedCharge().value().orElse(0); int allowedDischarge = this.getAllowedDischarge().value().orElse(0); @@ -124,7 +125,8 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), UNIT_ID, this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), UNIT_ID, this.cm, "Modbus", + config.modbus_id()); } @Deactivate @@ -137,49 +139,45 @@ protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC4ReadInputRegistersTask(0x100, Priority.HIGH, // m(RefuEss.ChannelId.SYSTEM_STATE, new UnsignedWordElement(0x100)), // - bm(new UnsignedWordElement(0x101))// - .m(RefuEss.ChannelId.STATE_0, 0) // - .m(RefuEss.ChannelId.STATE_1, 1) // - .m(RefuEss.ChannelId.STATE_2, 2) // - .m(RefuEss.ChannelId.STATE_3, 3) // - .m(RefuEss.ChannelId.STATE_4, 4) // - .m(RefuEss.ChannelId.STATE_5, 5) // - .m(RefuEss.ChannelId.STATE_6, 6) // - .m(RefuEss.ChannelId.STATE_7, 7) // - .m(RefuEss.ChannelId.STATE_8, 8) // - .m(RefuEss.ChannelId.STATE_9, 9, BitConverter.INVERT) // - .m(RefuEss.ChannelId.STATE_10, 10, BitConverter.INVERT) // - .build(), // - bm(new UnsignedWordElement(0x102))// - .m(RefuEss.ChannelId.STATE_11, 0, BitConverter.INVERT) // - .m(RefuEss.ChannelId.STATE_12, 1, BitConverter.INVERT) // - .m(RefuEss.ChannelId.STATE_13, 2, BitConverter.INVERT) // - .m(RefuEss.ChannelId.STATE_14, 3) // - .m(RefuEss.ChannelId.STATE_15, 4, BitConverter.INVERT) // - .build(), // - bm(new UnsignedWordElement(0x103))// - .m(RefuEss.ChannelId.INVERTER_STATE_0, 0) // - .m(RefuEss.ChannelId.INVERTER_STATE_1, 1) // - .m(RefuEss.ChannelId.INVERTER_STATE_2, 2) // - .m(RefuEss.ChannelId.INVERTER_STATE_3, 3) // - .m(RefuEss.ChannelId.INVERTER_STATE_7, 7) // - .m(RefuEss.ChannelId.INVERTER_STATE_8, 8) // - .m(RefuEss.ChannelId.INVERTER_STATE_9, 9) // - .m(RefuEss.ChannelId.INVERTER_STATE_10, 10) // - .m(RefuEss.ChannelId.INVERTER_STATE_11, 11) // - .m(RefuEss.ChannelId.INVERTER_STATE_12, 12) // - .m(RefuEss.ChannelId.INVERTER_STATE_13, 13) // - .build(), // + m(new BitsWordElement(0x101, this) // + .bit(0, RefuEss.ChannelId.STATE_0) // + .bit(1, RefuEss.ChannelId.STATE_1) // + .bit(2, RefuEss.ChannelId.STATE_2) // + .bit(3, RefuEss.ChannelId.STATE_3) // + .bit(4, RefuEss.ChannelId.STATE_4) // + .bit(5, RefuEss.ChannelId.STATE_5) // + .bit(6, RefuEss.ChannelId.STATE_6) // + .bit(7, RefuEss.ChannelId.STATE_7) // + .bit(8, RefuEss.ChannelId.STATE_8) // + .bit(9, RefuEss.ChannelId.STATE_9, BitConverter.INVERT) // + .bit(10, RefuEss.ChannelId.STATE_10, BitConverter.INVERT)), // + m(new BitsWordElement(0x102, this) // + .bit(0, RefuEss.ChannelId.STATE_11, BitConverter.INVERT) // + .bit(1, RefuEss.ChannelId.STATE_12, BitConverter.INVERT) // + .bit(2, RefuEss.ChannelId.STATE_13, BitConverter.INVERT) // + .bit(3, RefuEss.ChannelId.STATE_14) // + .bit(4, RefuEss.ChannelId.STATE_15, BitConverter.INVERT)), // + m(new BitsWordElement(0x103, this) // + .bit(0, RefuEss.ChannelId.INVERTER_STATE_0) // + .bit(1, RefuEss.ChannelId.INVERTER_STATE_1) // + .bit(2, RefuEss.ChannelId.INVERTER_STATE_2) // + .bit(3, RefuEss.ChannelId.INVERTER_STATE_3) // + .bit(7, RefuEss.ChannelId.INVERTER_STATE_7) // + .bit(8, RefuEss.ChannelId.INVERTER_STATE_8) // + .bit(9, RefuEss.ChannelId.INVERTER_STATE_9) // + .bit(10, RefuEss.ChannelId.INVERTER_STATE_10) // + .bit(11, RefuEss.ChannelId.INVERTER_STATE_11) // + .bit(12, RefuEss.ChannelId.INVERTER_STATE_12) // + .bit(13, RefuEss.ChannelId.INVERTER_STATE_13)), m(RefuEss.ChannelId.INVERTER_ERROR_CODE, new UnsignedWordElement(0x104)), // - bm(new UnsignedWordElement(0x105))// - .m(RefuEss.ChannelId.DCDC_STATE_0, 0) // - .m(RefuEss.ChannelId.DCDC_STATE_1, 1) // - .m(RefuEss.ChannelId.DCDC_STATE_2, 2) // - .m(RefuEss.ChannelId.DCDC_STATE_3, 3) // - .m(RefuEss.ChannelId.DCDC_STATE_7, 7) // - .m(RefuEss.ChannelId.DCDC_STATE_8, 8) // - .m(RefuEss.ChannelId.DCDC_STATE_9, 9) // - .build(), // + m(new BitsWordElement(0x105, this) // + .bit(0, RefuEss.ChannelId.DCDC_STATE_0) // + .bit(1, RefuEss.ChannelId.DCDC_STATE_1) // + .bit(2, RefuEss.ChannelId.DCDC_STATE_2) // + .bit(3, RefuEss.ChannelId.DCDC_STATE_3) // + .bit(7, RefuEss.ChannelId.DCDC_STATE_7) // + .bit(8, RefuEss.ChannelId.DCDC_STATE_8) // + .bit(9, RefuEss.ChannelId.DCDC_STATE_9)), m(RefuEss.ChannelId.DCDC_ERROR_CODE, new UnsignedWordElement(0x106)), // m(RefuEss.ChannelId.BATTERY_CURRENT_PCS, new SignedWordElement(0x107), ElementToChannelConverter.SCALE_FACTOR_2), // @@ -237,167 +235,151 @@ protected ModbusProtocol defineModbusProtocol() { new SignedDoublewordElement(0x126).wordOrder(WordOrder.LSWMSW)), // m(RefuEss.ChannelId.BATTERY_DISCHARGE_ENERGY, new SignedDoublewordElement(0x128).wordOrder(WordOrder.LSWMSW)), // - bm(new UnsignedWordElement(0x12A)) // for all: 0:Stop, 1:Operation - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_0, 0) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_1, 1) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_2, 2) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_3, 3) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_4, 4) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_5, 5) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_6, 6) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_7, 7) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_8, 8) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_9, 9) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_10, 10) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_11, 11) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_12, 12) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_13, 13) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_14, 14) // - .m(RefuEss.ChannelId.BATTERY_ON_GRID_STATE_15, 15) // - .build(), // + m(new BitsWordElement(0x12A, this) // + .bit(0, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_0) // + .bit(1, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_1) // + .bit(2, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_2) // + .bit(3, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_3) // + .bit(4, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_4) // + .bit(5, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_5) // + .bit(6, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_6) // + .bit(7, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_7) // + .bit(8, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_8) // + .bit(9, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_9) // + .bit(10, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_10) // + .bit(11, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_11) // + .bit(12, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_12) // + .bit(13, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_13) // + .bit(14, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_14) // + .bit(15, RefuEss.ChannelId.BATTERY_ON_GRID_STATE_15)), m(RefuEss.ChannelId.BATTERY_HIGHEST_VOLTAGE, new UnsignedWordElement(0x12B)), // m(RefuEss.ChannelId.BATTERY_LOWEST_VOLTAGE, new UnsignedWordElement(0x12C)), // m(RefuEss.ChannelId.BATTERY_HIGHEST_TEMPERATURE, new SignedWordElement(0x12D)), // m(RefuEss.ChannelId.BATTERY_LOWEST_TEMPERATURE, new SignedWordElement(0x12E)), // m(RefuEss.ChannelId.BATTERY_STOP_REQUEST, new UnsignedWordElement(0x12F)), // - bm(new UnsignedWordElement(0x130))// - .m(RefuEss.ChannelId.STATE_16, 0) // - .m(RefuEss.ChannelId.STATE_17, 1) // - .m(RefuEss.ChannelId.STATE_18, 2) // - .m(RefuEss.ChannelId.STATE_19, 3) // - .m(RefuEss.ChannelId.STATE_20, 4) // - .m(RefuEss.ChannelId.STATE_21, 5) // - .m(RefuEss.ChannelId.STATE_22, 6) // - .m(RefuEss.ChannelId.STATE_23, 7) // - .m(RefuEss.ChannelId.STATE_24, 8) // - .m(RefuEss.ChannelId.STATE_25, 9) // - .m(RefuEss.ChannelId.STATE_26, 10) // - .m(RefuEss.ChannelId.STATE_27, 11) // - .m(RefuEss.ChannelId.STATE_28, 12) // - .m(RefuEss.ChannelId.STATE_29, 13) // - .m(RefuEss.ChannelId.STATE_30, 14) // - .build(), // - bm(new UnsignedWordElement(0x131))// - .m(RefuEss.ChannelId.STATE_31, 0) // - .m(RefuEss.ChannelId.STATE_32, 1) // - .m(RefuEss.ChannelId.STATE_33, 5) // - .m(RefuEss.ChannelId.STATE_34, 7) // - .build(), - bm(new UnsignedWordElement(0x132))// - .m(RefuEss.ChannelId.STATE_35, 0) // - .m(RefuEss.ChannelId.STATE_36, 1) // - .m(RefuEss.ChannelId.STATE_37, 2) // - .m(RefuEss.ChannelId.STATE_38, 3) // - .build(), - bm(new UnsignedWordElement(0x133))// - .m(RefuEss.ChannelId.STATE_39, 0) // - .m(RefuEss.ChannelId.STATE_40, 1) // - .m(RefuEss.ChannelId.STATE_41, 2) // - .m(RefuEss.ChannelId.STATE_42, 3) // - .build(), + m(new BitsWordElement(0x130, this) // + .bit(0, RefuEss.ChannelId.STATE_16) // + .bit(1, RefuEss.ChannelId.STATE_17) // + .bit(2, RefuEss.ChannelId.STATE_18) // + .bit(3, RefuEss.ChannelId.STATE_19) // + .bit(4, RefuEss.ChannelId.STATE_20) // + .bit(5, RefuEss.ChannelId.STATE_21) // + .bit(6, RefuEss.ChannelId.STATE_22) // + .bit(7, RefuEss.ChannelId.STATE_23) // + .bit(8, RefuEss.ChannelId.STATE_24) // + .bit(9, RefuEss.ChannelId.STATE_25) // + .bit(10, RefuEss.ChannelId.STATE_26) // + .bit(11, RefuEss.ChannelId.STATE_27) // + .bit(12, RefuEss.ChannelId.STATE_28) // + .bit(13, RefuEss.ChannelId.STATE_29) // + .bit(14, RefuEss.ChannelId.STATE_30)), + m(new BitsWordElement(0x131, this) // + .bit(0, RefuEss.ChannelId.STATE_31) // + .bit(1, RefuEss.ChannelId.STATE_32) // + .bit(5, RefuEss.ChannelId.STATE_33) // + .bit(7, RefuEss.ChannelId.STATE_34)), + m(new BitsWordElement(0x132, this) // + .bit(0, RefuEss.ChannelId.STATE_35) // + .bit(1, RefuEss.ChannelId.STATE_36) // + .bit(2, RefuEss.ChannelId.STATE_37) // + .bit(3, RefuEss.ChannelId.STATE_38)), + m(new BitsWordElement(0x133, this) // + .bit(0, RefuEss.ChannelId.STATE_39) // + .bit(1, RefuEss.ChannelId.STATE_40) // + .bit(2, RefuEss.ChannelId.STATE_41) // + .bit(3, RefuEss.ChannelId.STATE_42)), new DummyRegisterElement(0x134), // - bm(new UnsignedWordElement(0x135))// - .m(RefuEss.ChannelId.STATE_43, 0) // - .m(RefuEss.ChannelId.STATE_44, 1) // - .m(RefuEss.ChannelId.STATE_45, 2) // - .m(RefuEss.ChannelId.STATE_46, 3) // - .build(), - bm(new UnsignedWordElement(0x136))// - .m(RefuEss.ChannelId.STATE_47, 0) // - .m(RefuEss.ChannelId.STATE_48, 1) // - .m(RefuEss.ChannelId.STATE_49, 2) // - .m(RefuEss.ChannelId.STATE_50, 3) // - .build(), - bm(new UnsignedWordElement(0x137))// - .m(RefuEss.ChannelId.STATE_51, 0) // - .m(RefuEss.ChannelId.STATE_52, 1) // - .m(RefuEss.ChannelId.STATE_53, 2) // - .m(RefuEss.ChannelId.STATE_54, 3) // - .m(RefuEss.ChannelId.STATE_55, 4) // - .m(RefuEss.ChannelId.STATE_56, 5) // - .m(RefuEss.ChannelId.STATE_57, 10) // - .m(RefuEss.ChannelId.STATE_58, 11) // - .m(RefuEss.ChannelId.STATE_59, 12) // - .m(RefuEss.ChannelId.STATE_60, 13) // - .build(), - bm(new UnsignedWordElement(0x138))// - .m(RefuEss.ChannelId.STATE_61, 0) // - .m(RefuEss.ChannelId.STATE_62, 1) // - .m(RefuEss.ChannelId.STATE_63, 2) // - .m(RefuEss.ChannelId.STATE_64, 3) // - .build(), - bm(new UnsignedWordElement(0x139))// - .m(RefuEss.ChannelId.STATE_65, 0) // - .m(RefuEss.ChannelId.STATE_66, 1) // - .m(RefuEss.ChannelId.STATE_67, 2) // - .m(RefuEss.ChannelId.STATE_68, 3) // - .build(), - bm(new UnsignedWordElement(0x13A))// - .m(RefuEss.ChannelId.STATE_69, 0) // - .m(RefuEss.ChannelId.STATE_70, 1) // - .m(RefuEss.ChannelId.STATE_71, 2) // - .m(RefuEss.ChannelId.STATE_72, 3) // - .build(), - bm(new UnsignedWordElement(0x13B))// - .m(RefuEss.ChannelId.STATE_73, 0) // - .m(RefuEss.ChannelId.STATE_74, 1) // - .m(RefuEss.ChannelId.STATE_75, 2) // - .m(RefuEss.ChannelId.STATE_76, 3) // - .build(), - bm(new UnsignedWordElement(0x13C))// - .m(RefuEss.ChannelId.STATE_77, 0) // - .m(RefuEss.ChannelId.STATE_78, 1) // - .m(RefuEss.ChannelId.STATE_79, 2) // - .m(RefuEss.ChannelId.STATE_80, 3) // - .build(), + m(new BitsWordElement(0x135, this) // + .bit(0, RefuEss.ChannelId.STATE_43) // + .bit(1, RefuEss.ChannelId.STATE_44) // + .bit(2, RefuEss.ChannelId.STATE_45) // + .bit(3, RefuEss.ChannelId.STATE_46)), + m(new BitsWordElement(0x136, this) // + .bit(0, RefuEss.ChannelId.STATE_47) // + .bit(1, RefuEss.ChannelId.STATE_48) // + .bit(2, RefuEss.ChannelId.STATE_49) // + .bit(3, RefuEss.ChannelId.STATE_50)), + m(new BitsWordElement(0x137, this) // + .bit(0, RefuEss.ChannelId.STATE_51) // + .bit(1, RefuEss.ChannelId.STATE_52) // + .bit(2, RefuEss.ChannelId.STATE_53) // + .bit(3, RefuEss.ChannelId.STATE_54) // + .bit(4, RefuEss.ChannelId.STATE_55) // + .bit(5, RefuEss.ChannelId.STATE_56) // + .bit(10, RefuEss.ChannelId.STATE_57) // + .bit(11, RefuEss.ChannelId.STATE_58) // + .bit(12, RefuEss.ChannelId.STATE_59) // + .bit(13, RefuEss.ChannelId.STATE_60)), + m(new BitsWordElement(0x138, this) // + .bit(0, RefuEss.ChannelId.STATE_61) // + .bit(1, RefuEss.ChannelId.STATE_62) // + .bit(2, RefuEss.ChannelId.STATE_63) // + .bit(3, RefuEss.ChannelId.STATE_64)), + m(new BitsWordElement(0x139, this) // + .bit(0, RefuEss.ChannelId.STATE_65) // + .bit(1, RefuEss.ChannelId.STATE_66) // + .bit(2, RefuEss.ChannelId.STATE_67) // + .bit(3, RefuEss.ChannelId.STATE_68)), + m(new BitsWordElement(0x13A, this) // + .bit(0, RefuEss.ChannelId.STATE_69) // + .bit(1, RefuEss.ChannelId.STATE_70) // + .bit(2, RefuEss.ChannelId.STATE_71) // + .bit(3, RefuEss.ChannelId.STATE_72)), + m(new BitsWordElement(0x13B, this) // + .bit(0, RefuEss.ChannelId.STATE_73) // + .bit(1, RefuEss.ChannelId.STATE_74) // + .bit(2, RefuEss.ChannelId.STATE_75) // + .bit(3, RefuEss.ChannelId.STATE_76)), + m(new BitsWordElement(0x13C, this) // + .bit(0, RefuEss.ChannelId.STATE_77) // + .bit(1, RefuEss.ChannelId.STATE_78) // + .bit(2, RefuEss.ChannelId.STATE_79) // + .bit(3, RefuEss.ChannelId.STATE_80)), new DummyRegisterElement(0x13D), // new DummyRegisterElement(0x13E), // - bm(new UnsignedWordElement(0x13F))// - .m(RefuEss.ChannelId.STATE_81, 2) // - .m(RefuEss.ChannelId.STATE_82, 3) // - .m(RefuEss.ChannelId.STATE_83, 4) // - .m(RefuEss.ChannelId.STATE_84, 6) // - .m(RefuEss.ChannelId.STATE_85, 9) // - .m(RefuEss.ChannelId.STATE_86, 10) // - .m(RefuEss.ChannelId.STATE_87, 11) // - .m(RefuEss.ChannelId.STATE_88, 12) // - .m(RefuEss.ChannelId.STATE_89, 13) // - .m(RefuEss.ChannelId.STATE_90, 14) // - .m(RefuEss.ChannelId.STATE_91, 15) // - .build(), - bm(new UnsignedWordElement(0x140))// - .m(RefuEss.ChannelId.STATE_92, 2) // - .m(RefuEss.ChannelId.STATE_93, 3) // - .m(RefuEss.ChannelId.STATE_94, 7) // - .m(RefuEss.ChannelId.STATE_95, 8) // - .m(RefuEss.ChannelId.STATE_96, 10) // - .m(RefuEss.ChannelId.STATE_97, 11) // - .m(RefuEss.ChannelId.STATE_98, 12) // - .m(RefuEss.ChannelId.STATE_99, 13) // - .m(RefuEss.ChannelId.STATE_100, 14) // - .build(), + m(new BitsWordElement(0x13F, this) // + .bit(2, RefuEss.ChannelId.STATE_81) // + .bit(3, RefuEss.ChannelId.STATE_82) // + .bit(4, RefuEss.ChannelId.STATE_83) // + .bit(6, RefuEss.ChannelId.STATE_84) // + .bit(9, RefuEss.ChannelId.STATE_85) // + .bit(10, RefuEss.ChannelId.STATE_86) // + .bit(11, RefuEss.ChannelId.STATE_87) // + .bit(12, RefuEss.ChannelId.STATE_88) // + .bit(13, RefuEss.ChannelId.STATE_89) // + .bit(14, RefuEss.ChannelId.STATE_90) // + .bit(15, RefuEss.ChannelId.STATE_91)), + m(new BitsWordElement(0x140, this) // + .bit(2, RefuEss.ChannelId.STATE_92) // + .bit(3, RefuEss.ChannelId.STATE_93) // + .bit(7, RefuEss.ChannelId.STATE_94) // + .bit(8, RefuEss.ChannelId.STATE_95) // + .bit(10, RefuEss.ChannelId.STATE_96) // + .bit(11, RefuEss.ChannelId.STATE_97) // + .bit(12, RefuEss.ChannelId.STATE_98) // + .bit(13, RefuEss.ChannelId.STATE_99) // + .bit(14, RefuEss.ChannelId.STATE_100)), new DummyRegisterElement(0x141), // new DummyRegisterElement(0x142), // new DummyRegisterElement(0x143), // new DummyRegisterElement(0x144), // - bm(new UnsignedWordElement(0x145)) // for all: 1:Stop, 0:Operation - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_0, 0) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_1, 1) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_2, 2) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_3, 3) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_4, 4) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_5, 5) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_6, 6) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_7, 7) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_8, 8) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_9, 9) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_10, 10) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_11, 11) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_12, 12) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_13, 13) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_14, 14) // - .m(RefuEss.ChannelId.BATTERY_CONTROL_STATE_15, 15) // - .build(), // + m(new BitsWordElement(0x145, this) // + .bit(0, RefuEss.ChannelId.BATTERY_CONTROL_STATE_0) // + .bit(1, RefuEss.ChannelId.BATTERY_CONTROL_STATE_1) // + .bit(2, RefuEss.ChannelId.BATTERY_CONTROL_STATE_2) // + .bit(3, RefuEss.ChannelId.BATTERY_CONTROL_STATE_3) // + .bit(4, RefuEss.ChannelId.BATTERY_CONTROL_STATE_4) // + .bit(5, RefuEss.ChannelId.BATTERY_CONTROL_STATE_5) // + .bit(6, RefuEss.ChannelId.BATTERY_CONTROL_STATE_6) // + .bit(7, RefuEss.ChannelId.BATTERY_CONTROL_STATE_7) // + .bit(8, RefuEss.ChannelId.BATTERY_CONTROL_STATE_8) // + .bit(9, RefuEss.ChannelId.BATTERY_CONTROL_STATE_9) // + .bit(10, RefuEss.ChannelId.BATTERY_CONTROL_STATE_10) // + .bit(11, RefuEss.ChannelId.BATTERY_CONTROL_STATE_11) // + .bit(12, RefuEss.ChannelId.BATTERY_CONTROL_STATE_12) // + .bit(13, RefuEss.ChannelId.BATTERY_CONTROL_STATE_13) // + .bit(14, RefuEss.ChannelId.BATTERY_CONTROL_STATE_14) // + .bit(15, RefuEss.ChannelId.BATTERY_CONTROL_STATE_15)), m(RefuEss.ChannelId.ERROR_LOG_0, new UnsignedWordElement(0x146)), // m(RefuEss.ChannelId.ERROR_LOG_1, new UnsignedWordElement(0x147)), // m(RefuEss.ChannelId.ERROR_LOG_2, new UnsignedWordElement(0x148)), // @@ -982,14 +964,14 @@ public Constraint[] getStaticConstraints() { } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - ManagedSymmetricEss.getModbusSlaveNatureTable(), // - AsymmetricEss.getModbusSlaveNatureTable(), // - ManagedAsymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(RefuEss.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // + AsymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedAsymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(RefuEss.class, accessMode, 300) // .build()); } } diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SetOperationMode.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SetOperationMode.java index c38fcf7f1f1..e5294f7cdd0 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SetOperationMode.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SetOperationMode.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.refu; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SetOperationMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/StopStart.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/StopStart.java index beb4270faf3..21be28dc993 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/StopStart.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/StopStart.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.refu; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; enum StopStart implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SystemState.java b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SystemState.java index 2d77ca5edb9..c664acfdccf 100644 --- a/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SystemState.java +++ b/io.openems.edge.ess.refu/src/io/openems/edge/ess/refu/SystemState.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.refu; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; enum SystemState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sinexcel/.gitignore b/io.openems.edge.ess.sinexcel/.gitignore index 9e0adcc107c..36573722b7e 100644 --- a/io.openems.edge.ess.sinexcel/.gitignore +++ b/io.openems.edge.ess.sinexcel/.gitignore @@ -1 +1,2 @@ /generated/ +/bin_test/ diff --git a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/Config.java b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/Config.java index 3ba3debc2e5..cabbb141ebe 100644 --- a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/Config.java +++ b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/Config.java @@ -8,10 +8,14 @@ name = "ESS Sinexcel", // description = "Implements the Sinexcel battery inverter.") @interface Config { - String service_pid(); + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Modbus-ID", description = "ID of Modbus brige.") diff --git a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/CurrentState.java b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/CurrentState.java index b1042d8af32..02c10489386 100644 --- a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/CurrentState.java +++ b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/CurrentState.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.sinexcel; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum CurrentState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/EssSinexcel.java b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/EssSinexcel.java index 240ebdfbda5..877b74167da 100644 --- a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/EssSinexcel.java +++ b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/EssSinexcel.java @@ -19,12 +19,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.battery.api.Battery; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; import io.openems.edge.bridge.modbus.api.element.SignedWordElement; import io.openems.edge.bridge.modbus.api.element.StringWordElement; @@ -88,12 +89,13 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), DEFAULT_UNIT_ID, this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), DEFAULT_UNIT_ID, this.cm, "Modbus", + config.modbus_id()); this.inverterState = config.InverterState(); // initialize the connection to the battery - this.initializeBattery(config.service_pid(), config.battery_id()); + this.initializeBattery(config.battery_id()); this.softStart(); this.resetDcAcEnergy(); @@ -122,8 +124,8 @@ public EssSinexcel() { * @param servicePid this components' Service-PID * @param batteryId the Component-ID of the Battery component */ - private void initializeBattery(String servicePid, String batteryId) { - if (OpenemsComponent.updateReferenceFilter(this.cm, servicePid, "Battery", batteryId)) { + private void initializeBattery(String batteryId) { + if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "Battery", batteryId)) { return; } @@ -161,7 +163,7 @@ private void setBatteryRanges() { setChaMaxV.setNextWriteValue(chaMaxV * 10); this.channel(SinexcelChannelId.STATE_UNABLE_TO_SET_BATTERY_RANGES).setNextValue(false); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "Unable to set battery ranges: " + e.getMessage()); this.channel(SinexcelChannelId.STATE_UNABLE_TO_SET_BATTERY_RANGES).setNextValue(false); } @@ -174,7 +176,7 @@ public void inverterOn() { IntegerWriteChannel setdataModOnCmd = this.channel(SinexcelChannelId.SETDATA_MOD_ON_CMD); try { setdataModOnCmd.setNextWriteValue(1); // Here: START = 1 - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "problem occurred while trying to start inverter" + e.getMessage()); } } @@ -186,7 +188,7 @@ public void inverterOff() { IntegerWriteChannel setdataModOffCmd = this.channel(SinexcelChannelId.SETDATA_MOD_OFF_CMD); try { setdataModOffCmd.setNextWriteValue(1); // Here: STOP = 1 - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "problem occurred while trying to stop system" + e.getMessage()); } } @@ -201,7 +203,7 @@ public void resetDcAcEnergy() { dischargeDcEnergy.setNextWriteValue(0); chargeEnergy.setNextWriteValue(0); dischargeEnergy.setNextWriteValue(0); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "problem occurred while trying to reset the AC DC energy" + e.getMessage()); } } @@ -213,7 +215,7 @@ public void softStart() { IntegerWriteChannel setDcRelay = this.channel(SinexcelChannelId.SET_INTERN_DC_RELAY); try { setDcRelay.setNextWriteValue(1); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "problem occured while trying to set the intern DC relay"); } } @@ -228,7 +230,7 @@ public void islandOn() { try { setAntiIslanding.setNextWriteValue(DISABLED_ANTI_ISLANDING); setdataGridOffCmd.setNextWriteValue(1); // Stop - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "problem occurred while trying to activate" + e.getMessage()); } } @@ -243,7 +245,7 @@ public void islandingOff() { try { setAntiIslanding.setNextWriteValue(ENABLED_ANTI_ISLANDING); setdataGridOnCmd.setNextWriteValue(1); // Start - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "problem occurred while trying to deactivate islanding" + e.getMessage()); } } @@ -254,7 +256,7 @@ public void doHandlingSlowFloatVoltage() { try { setSlowChargeVoltage.setNextWriteValue(SLOW_CHARGE_VOLTAGE); setFloatChargeVoltage.setNextWriteValue(FLOAT_CHARGE_VOLTAGE); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(this.log, "problem occurred while trying to write the voltage limits" + e.getMessage()); } } @@ -394,131 +396,119 @@ protected ModbusProtocol defineModbusProtocol() { // Line136, Magnification = 0 m(SymmetricEss.ChannelId.REACTIVE_POWER, new SignedWordElement(0x024E))), new FC3ReadRegistersTask(0x0262, Priority.LOW, // - bm(new UnsignedWordElement(0x0262)) // - .m(SinexcelChannelId.STATE_0, 0) // - .m(SinexcelChannelId.STATE_1, 1) // - .m(SinexcelChannelId.STATE_2, 2) // - .m(SinexcelChannelId.STATE_3, 3) // - .m(SinexcelChannelId.STATE_4, 4) // Grid shutdown - .m(SinexcelChannelId.STATE_5, 5) // - .m(SinexcelChannelId.STATE_6, 6) // - .m(SinexcelChannelId.STATE_7, 7) // - .m(SinexcelChannelId.STATE_8, 8) // - .m(SinexcelChannelId.STATE_9, 9) // - .m(SinexcelChannelId.STATE_10, 10) // - .m(SinexcelChannelId.STATE_11, 11) // - .m(SinexcelChannelId.STATE_12, 12) // - .m(SinexcelChannelId.STATE_13, 13) // - .m(SinexcelChannelId.STATE_14, 14) // - .m(SinexcelChannelId.STATE_15, 15) // - .build()), // + m(new BitsWordElement(0x0262, this) // + .bit(0, SinexcelChannelId.STATE_0) // + .bit(1, SinexcelChannelId.STATE_1) // + .bit(2, SinexcelChannelId.STATE_2) // + .bit(3, SinexcelChannelId.STATE_3) // + .bit(4, SinexcelChannelId.STATE_4) // + .bit(5, SinexcelChannelId.STATE_5) // + .bit(6, SinexcelChannelId.STATE_6) // + .bit(7, SinexcelChannelId.STATE_7) // + .bit(8, SinexcelChannelId.STATE_8) // + .bit(9, SinexcelChannelId.STATE_9) // + .bit(10, SinexcelChannelId.STATE_10) // + .bit(11, SinexcelChannelId.STATE_11) // + .bit(12, SinexcelChannelId.STATE_12) // + .bit(13, SinexcelChannelId.STATE_13) // + .bit(14, SinexcelChannelId.STATE_14) // + .bit(15, SinexcelChannelId.STATE_15))), new FC3ReadRegistersTask(0x0260, Priority.LOW, // - bm(new UnsignedWordElement(0x0260)) // - .m(SinexcelChannelId.SINEXCEL_STATE_1, 1) // - .m(SinexcelChannelId.SINEXCEL_STATE_2, 2) // - .m(SinexcelChannelId.SINEXCEL_STATE_3, 3) // - .m(SinexcelChannelId.SINEXCEL_STATE_4, 4) // - .m(SinexcelChannelId.SINEXCEL_STATE_5, 5) // - .m(SinexcelChannelId.SINEXCEL_STATE_6, 6) // - .m(SinexcelChannelId.SINEXCEL_STATE_7, 7) // - .m(SinexcelChannelId.SINEXCEL_STATE_8, 8) // - .m(SinexcelChannelId.SINEXCEL_STATE_9, 9) // - .build()), // + m(new BitsWordElement(0x0260, this) // + .bit(1, SinexcelChannelId.SINEXCEL_STATE_1) // + .bit(2, SinexcelChannelId.SINEXCEL_STATE_2) // + .bit(3, SinexcelChannelId.SINEXCEL_STATE_3) // + .bit(4, SinexcelChannelId.SINEXCEL_STATE_4) // + .bit(5, SinexcelChannelId.SINEXCEL_STATE_5) // + .bit(6, SinexcelChannelId.SINEXCEL_STATE_6) // + .bit(7, SinexcelChannelId.SINEXCEL_STATE_7) // + .bit(8, SinexcelChannelId.SINEXCEL_STATE_8) // + .bit(9, SinexcelChannelId.SINEXCEL_STATE_9))), new FC3ReadRegistersTask(0x0020, Priority.LOW, // - bm(new UnsignedWordElement(0x0020)) // - .m(SinexcelChannelId.STATE_16, 0) // - .m(SinexcelChannelId.STATE_17, 1) // - .m(SinexcelChannelId.STATE_18, 2) // - .m(SinexcelChannelId.STATE_19, 3) // - .m(SinexcelChannelId.STATE_20, 4) // - .build()), // + m(new BitsWordElement(0x0020, this) // + .bit(0, SinexcelChannelId.STATE_16) // + .bit(1, SinexcelChannelId.STATE_17) // + .bit(2, SinexcelChannelId.STATE_18) // + .bit(3, SinexcelChannelId.STATE_19) // + .bit(4, SinexcelChannelId.STATE_20))), new FC3ReadRegistersTask(0x0024, Priority.LOW, // - bm(new UnsignedWordElement(0x0024)) // - .m(SinexcelChannelId.STATE_21, 0) // - .m(SinexcelChannelId.STATE_22, 1) // - .m(SinexcelChannelId.STATE_23, 2) // - .m(SinexcelChannelId.STATE_24, 3) // - .m(SinexcelChannelId.STATE_25, 4) // - .m(SinexcelChannelId.STATE_26, 5) // - .m(SinexcelChannelId.STATE_27, 6) // - .m(SinexcelChannelId.STATE_28, 7) // - .m(SinexcelChannelId.STATE_29, 8) // - .m(SinexcelChannelId.STATE_30, 9) // - .m(SinexcelChannelId.STATE_31, 10) // - .m(SinexcelChannelId.STATE_32, 11) // - .m(SinexcelChannelId.STATE_33, 12) // - .build()), // + m(new BitsWordElement(0x0024, this) // + .bit(0, SinexcelChannelId.STATE_21) // + .bit(1, SinexcelChannelId.STATE_22) // + .bit(2, SinexcelChannelId.STATE_23) // + .bit(3, SinexcelChannelId.STATE_24) // + .bit(4, SinexcelChannelId.STATE_25) // + .bit(5, SinexcelChannelId.STATE_26) // + .bit(6, SinexcelChannelId.STATE_27) // + .bit(7, SinexcelChannelId.STATE_28) // + .bit(8, SinexcelChannelId.STATE_29) // + .bit(9, SinexcelChannelId.STATE_30) // + .bit(10, SinexcelChannelId.STATE_31) // + .bit(11, SinexcelChannelId.STATE_32) // + .bit(12, SinexcelChannelId.STATE_33))), new FC3ReadRegistersTask(0x0025, Priority.LOW, // - bm(new UnsignedWordElement(0x0025)) // - .m(SinexcelChannelId.STATE_34, 0) // - .m(SinexcelChannelId.STATE_35, 1) // - .m(SinexcelChannelId.STATE_36, 2) // - .m(SinexcelChannelId.STATE_37, 3) // - .m(SinexcelChannelId.STATE_38, 4) // - .m(SinexcelChannelId.STATE_39, 5) // - .m(SinexcelChannelId.STATE_40, 6) // - .m(SinexcelChannelId.STATE_41, 7) // - .m(SinexcelChannelId.STATE_42, 8) // - .m(SinexcelChannelId.STATE_43, 9) // - .m(SinexcelChannelId.STATE_44, 10) // - .m(SinexcelChannelId.STATE_45, 11) // - .m(SinexcelChannelId.STATE_47, 13) // - .m(SinexcelChannelId.STATE_48, 14) // - .m(SinexcelChannelId.STATE_49, 15) // - .build()), // + m(new BitsWordElement(0x0025, this) // + .bit(0, SinexcelChannelId.STATE_34) // + .bit(1, SinexcelChannelId.STATE_35) // + .bit(2, SinexcelChannelId.STATE_36) // + .bit(3, SinexcelChannelId.STATE_37) // + .bit(4, SinexcelChannelId.STATE_38) // + .bit(5, SinexcelChannelId.STATE_39) // + .bit(6, SinexcelChannelId.STATE_40) // + .bit(7, SinexcelChannelId.STATE_41) // + .bit(8, SinexcelChannelId.STATE_42) // + .bit(9, SinexcelChannelId.STATE_43) // + .bit(10, SinexcelChannelId.STATE_44) // + .bit(11, SinexcelChannelId.STATE_45) // + .bit(13, SinexcelChannelId.STATE_47) // + .bit(14, SinexcelChannelId.STATE_48) // + .bit(15, SinexcelChannelId.STATE_49))), new FC3ReadRegistersTask(0x0026, Priority.LOW, // - bm(new UnsignedWordElement(0x0026)) // - .m(SinexcelChannelId.STATE_50, 0) // - .m(SinexcelChannelId.STATE_52, 2) // - .m(SinexcelChannelId.STATE_53, 3) // - .m(SinexcelChannelId.STATE_54, 4) // - .build()), // + m(new BitsWordElement(0x0026, this) // + .bit(0, SinexcelChannelId.STATE_50) // + .bit(2, SinexcelChannelId.STATE_52) // + .bit(3, SinexcelChannelId.STATE_53) // + .bit(4, SinexcelChannelId.STATE_54))), new FC3ReadRegistersTask(0x0027, Priority.LOW, // - bm(new UnsignedWordElement(0x0027)) // - .m(SinexcelChannelId.STATE_55, 0) // - .m(SinexcelChannelId.STATE_56, 1) // - .m(SinexcelChannelId.STATE_57, 2) // - .m(SinexcelChannelId.STATE_58, 3) // - .build()), // + m(new BitsWordElement(0x0027, this) // + .bit(0, SinexcelChannelId.STATE_55) // + .bit(1, SinexcelChannelId.STATE_56) // + .bit(2, SinexcelChannelId.STATE_57) // + .bit(3, SinexcelChannelId.STATE_58))), new FC3ReadRegistersTask(0x0028, Priority.LOW, // - bm(new UnsignedWordElement(0x0028)) // - .m(SinexcelChannelId.STATE_59, 0) // - .m(SinexcelChannelId.STATE_60, 1) // - .m(SinexcelChannelId.STATE_61, 2) // - .m(SinexcelChannelId.STATE_62, 3) // - .m(SinexcelChannelId.STATE_63, 4) // - .m(SinexcelChannelId.STATE_64, 5) // - .build()), // + m(new BitsWordElement(0x0028, this) // + .bit(0, SinexcelChannelId.STATE_59) // + .bit(1, SinexcelChannelId.STATE_60) // + .bit(2, SinexcelChannelId.STATE_61) // + .bit(3, SinexcelChannelId.STATE_62) // + .bit(4, SinexcelChannelId.STATE_63) // + .bit(5, SinexcelChannelId.STATE_64))), new FC3ReadRegistersTask(0x002B, Priority.LOW, // - bm(new UnsignedWordElement(0x002B)) // - .m(SinexcelChannelId.STATE_65, 0) // - .m(SinexcelChannelId.STATE_66, 1) // - .m(SinexcelChannelId.STATE_67, 2) // - .m(SinexcelChannelId.STATE_68, 3) // - .build()), // + m(new BitsWordElement(0x002B, this) // + .bit(0, SinexcelChannelId.STATE_65) // + .bit(1, SinexcelChannelId.STATE_66) // + .bit(2, SinexcelChannelId.STATE_67) // + .bit(3, SinexcelChannelId.STATE_68))), new FC3ReadRegistersTask(0x002C, Priority.LOW, // - bm(new UnsignedWordElement(0x002C)) // - .m(SinexcelChannelId.STATE_69, 0) // - .m(SinexcelChannelId.STATE_70, 1) // - .m(SinexcelChannelId.STATE_71, 2) // - .m(SinexcelChannelId.STATE_72, 3) // - .m(SinexcelChannelId.STATE_73, 4) // - .build()), // + m(new BitsWordElement(0x002C, this) // + .bit(0, SinexcelChannelId.STATE_69) // + .bit(1, SinexcelChannelId.STATE_70) // + .bit(2, SinexcelChannelId.STATE_71) // + .bit(3, SinexcelChannelId.STATE_72) // + .bit(4, SinexcelChannelId.STATE_73))), new FC3ReadRegistersTask(0x002F, Priority.LOW, // - bm(new UnsignedWordElement(0x002F)) // - .m(SinexcelChannelId.STATE_74, 0) // - .build()) // - ); + m(new BitsWordElement(0x002F, this) // + .bit(0, SinexcelChannelId.STATE_74)))); } @Override @@ -544,7 +534,7 @@ public Constraint[] getStaticConstraints() { } @Override - public void applyPower(int activePower, int reactivePower) throws OpenemsException { + public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException { switch (this.inverterState) { case ON: /* diff --git a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/SinexcelChannelId.java b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/SinexcelChannelId.java index 4424a936dd0..cceda3f8721 100644 --- a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/SinexcelChannelId.java +++ b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/SinexcelChannelId.java @@ -2,15 +2,15 @@ import java.util.Optional; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.ChannelId; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.IntegerDoc; import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.Level; import io.openems.edge.common.channel.StateChannel; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.sum.GridMode; public enum SinexcelChannelId implements ChannelId { diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/Config.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/Config.java index 9ed88cbf3c0..b78592ec9e4 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/Config.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/Config.java @@ -10,8 +10,13 @@ description = "Implements the SMA SunnyIsland 6.0H energy storage system.") @interface Config { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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; SinglePhase phase() default SinglePhase.L1; diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/SiChannelId.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/SiChannelId.java index d907920b00a..dabcae9bb08 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/SiChannelId.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/SiChannelId.java @@ -1,10 +1,10 @@ package io.openems.edge.sma; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.ChannelId; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.sma.enums.AbsorptionPhaseActive; import io.openems.edge.sma.enums.AcknowledgeGeneratorErrors; import io.openems.edge.sma.enums.ActiveBatteryChargingMode; diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/SunnyIsland6Ess.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/SunnyIsland6Ess.java index 37952723d98..06166fd8661 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/SunnyIsland6Ess.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/SunnyIsland6Ess.java @@ -76,7 +76,7 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) throws OpenemsException { - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); this.phase = config.phase(); SinglePhaseEss.initializeCopyPhaseChannel(this, this.phase); @@ -89,7 +89,7 @@ protected void deactivate() { // TODO IMP!! LOAD_POWER "30861" @Override - public void applyPower(int activePower, int reactivePower) throws OpenemsException { + public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException { EnumWriteChannel setControlMode = this.channel(SiChannelId.SET_CONTROL_MODE); IntegerWriteChannel setActivePowerChannel = this.channel(SiChannelId.SET_ACTIVE_POWER); IntegerWriteChannel setReactivePowerChannel = this.channel(SiChannelId.SET_REACTIVE_POWER); diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AbsorptionPhaseActive.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AbsorptionPhaseActive.java index d095a7f4775..5ba6b596ae2 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AbsorptionPhaseActive.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AbsorptionPhaseActive.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum AbsorptionPhaseActive implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AcknowledgeGeneratorErrors.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AcknowledgeGeneratorErrors.java index 56e1cc0a7cd..ede35941c12 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AcknowledgeGeneratorErrors.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AcknowledgeGeneratorErrors.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum AcknowledgeGeneratorErrors implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ActiveBatteryChargingMode.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ActiveBatteryChargingMode.java index c9c84ca90f8..a37a3a4274c 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ActiveBatteryChargingMode.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ActiveBatteryChargingMode.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ActiveBatteryChargingMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticFrequencySynchronization.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticFrequencySynchronization.java index 3f09fb1cd28..57d99c7531f 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticFrequencySynchronization.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticFrequencySynchronization.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum AutomaticFrequencySynchronization implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticGeneratorStart.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticGeneratorStart.java index b93ad63424a..0c201ae4e74 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticGeneratorStart.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/AutomaticGeneratorStart.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum AutomaticGeneratorStart implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BMSOperatingMode.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BMSOperatingMode.java index 0a22b9cb45a..a404b74e5ef 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BMSOperatingMode.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BMSOperatingMode.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BMSOperatingMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BatteryType.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BatteryType.java index d059fed6595..62a76897d52 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BatteryType.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/BatteryType.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BatteryType implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiEndPoint.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiEndPoint.java index 7cf02f68190..584844b086d 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiEndPoint.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiEndPoint.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ConfigurationOfTheCosphiEndPoint implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiStartingPoint.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiStartingPoint.java index f19591076a7..43a451bada9 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiStartingPoint.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ConfigurationOfTheCosphiStartingPoint.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ConfigurationOfTheCosphiStartingPoint implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ControlOfBatteryChargingViaCommunicationAvailable.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ControlOfBatteryChargingViaCommunicationAvailable.java index 5f9f32a2160..6e8d8f04309 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ControlOfBatteryChargingViaCommunicationAvailable.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ControlOfBatteryChargingViaCommunicationAvailable.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ControlOfBatteryChargingViaCommunicationAvailable implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DataTransferRateOfNetworkTerminalA.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DataTransferRateOfNetworkTerminalA.java index 99240615e8d..f0d17388f26 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DataTransferRateOfNetworkTerminalA.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DataTransferRateOfNetworkTerminalA.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum DataTransferRateOfNetworkTerminalA implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DuplexModeOfNetworkTerminalA.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DuplexModeOfNetworkTerminalA.java index f00a1f9b277..4ceb05dda7d 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DuplexModeOfNetworkTerminalA.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/DuplexModeOfNetworkTerminalA.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum DuplexModeOfNetworkTerminalA implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GeneratorStatus.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GeneratorStatus.java index 47a07f0e7ab..4d828b9f7b6 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GeneratorStatus.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GeneratorStatus.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum GeneratorStatus implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridCreatingGenerator.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridCreatingGenerator.java index 244dc1656d5..a8058e78765 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridCreatingGenerator.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridCreatingGenerator.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum GridCreatingGenerator implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViPowerSwitchOn.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViPowerSwitchOn.java index 40645511050..802ec383dc4 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViPowerSwitchOn.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViPowerSwitchOn.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum GridRequestViPowerSwitchOn implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViaChargeType.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViaChargeType.java index 927d8ddabac..29fb1262142 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViaChargeType.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/GridRequestViaChargeType.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum GridRequestViaChargeType implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualControlOfNetworkConnection.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualControlOfNetworkConnection.java index 92d5bd5c550..09056974953 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualControlOfNetworkConnection.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualControlOfNetworkConnection.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ManualControlOfNetworkConnection implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualEqualizationCharge.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualEqualizationCharge.java index 1bc8fe0aad2..eadc7f28349 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualEqualizationCharge.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualEqualizationCharge.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ManualEqualizationCharge implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualGeneratorStart.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualGeneratorStart.java index 02f61e2c8dd..bfd00e1bcbb 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualGeneratorStart.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ManualGeneratorStart.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ManualGeneratorStart implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MemoryCardStatus.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MemoryCardStatus.java index c86981bd49a..3eeb6d4da24 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MemoryCardStatus.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MemoryCardStatus.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum MemoryCardStatus implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MeterSetting.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MeterSetting.java index 544dbfc9b81..ca216ff77f8 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MeterSetting.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MeterSetting.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum MeterSetting implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MultifunctionRelayStatus.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MultifunctionRelayStatus.java index 966d795d874..9cb0abce02d 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MultifunctionRelayStatus.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/MultifunctionRelayStatus.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum MultifunctionRelayStatus implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeForActivePowerLimitation.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeForActivePowerLimitation.java index b55e3446368..d1dd9e2a776 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeForActivePowerLimitation.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeForActivePowerLimitation.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum OperatingModeForActivePowerLimitation implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeOfActivePowerLimitationAtOverFrequency.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeOfActivePowerLimitationAtOverFrequency.java index b77a7f5445a..658d0f735c3 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeOfActivePowerLimitationAtOverFrequency.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/OperatingModeOfActivePowerLimitationAtOverFrequency.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum OperatingModeOfActivePowerLimitationAtOverFrequency implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerFeedbackToPublicGridAllowed.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerFeedbackToPublicGridAllowed.java index 78bdbc32a22..794ad37473b 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerFeedbackToPublicGridAllowed.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerFeedbackToPublicGridAllowed.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PowerFeedbackToPublicGridAllowed implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerSupplyStatus.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerSupplyStatus.java index c44eac030d8..a663359caaa 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerSupplyStatus.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PowerSupplyStatus.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PowerSupplyStatus implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PvMainsConnection.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PvMainsConnection.java index b2c81c9321e..3b001978f7f 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PvMainsConnection.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/PvMainsConnection.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PvMainsConnection implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ReasonForGeneratorRequest.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ReasonForGeneratorRequest.java index 8e5f7bf246e..29941c25ded 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ReasonForGeneratorRequest.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/ReasonForGeneratorRequest.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ReasonForGeneratorRequest implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheControlledInverter.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheControlledInverter.java index f3bdc742c5a..5eaf067c063 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheControlledInverter.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheControlledInverter.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum RepetitionCycleOfTheControlledInverter implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheTimeControlledGeneratorOperation.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheTimeControlledGeneratorOperation.java index 0d539ffbdaa..182b5343058 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheTimeControlledGeneratorOperation.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RepetitionCycleOfTheTimeControlledGeneratorOperation.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum RepetitionCycleOfTheTimeControlledGeneratorOperation implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RiseInSelfConsumptionSwitchedOn.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RiseInSelfConsumptionSwitchedOn.java index 59a28429578..7a8cd711384 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RiseInSelfConsumptionSwitchedOn.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/RiseInSelfConsumptionSwitchedOn.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum RiseInSelfConsumptionSwitchedOn implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SetControlMode.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SetControlMode.java index 2f0f33f8a6e..00b3f6393b6 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SetControlMode.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SetControlMode.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SetControlMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SpeedWireConnectionStatusOfNetworkTerminalA.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SpeedWireConnectionStatusOfNetworkTerminalA.java index e57bee14d7d..7b97a2d594a 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SpeedWireConnectionStatusOfNetworkTerminalA.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SpeedWireConnectionStatusOfNetworkTerminalA.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SpeedWireConnectionStatusOfNetworkTerminalA implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusBatteryApplicationArea.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusBatteryApplicationArea.java index 9e1c85b93fe..f6fd227e63f 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusBatteryApplicationArea.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusBatteryApplicationArea.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum StatusBatteryApplicationArea implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusDigitalInput.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusDigitalInput.java index 91c165a1ad7..58c7bbd7c56 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusDigitalInput.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusDigitalInput.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum StatusDigitalInput implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusOfUtilityGrid.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusOfUtilityGrid.java index a83894ed79c..b192a3b2eae 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusOfUtilityGrid.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/StatusOfUtilityGrid.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum StatusOfUtilityGrid implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SystemState.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SystemState.java index 06d7ea67502..ce0456150e4 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SystemState.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/SystemState.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SystemState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledGeneratorOperation.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledGeneratorOperation.java index b6c5c850ec1..27109544b59 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledGeneratorOperation.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledGeneratorOperation.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum TimeControlledGeneratorOperation implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledInverterOperation.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledInverterOperation.java index b39959cfb29..b094cc50f84 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledInverterOperation.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TimeControlledInverterOperation.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum TimeControlledInverterOperation implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TypeOfACSubdistribution.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TypeOfACSubdistribution.java index 9a9148eb63e..37e37874b80 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TypeOfACSubdistribution.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/enums/TypeOfACSubdistribution.java @@ -1,6 +1,6 @@ package io.openems.edge.sma.enums; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum TypeOfACSubdistribution implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/AbstractEssStreetscooter.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/AbstractEssStreetscooter.java index 450f24f3728..b1a2032f3e9 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/AbstractEssStreetscooter.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/AbstractEssStreetscooter.java @@ -4,6 +4,7 @@ import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; +import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; @@ -21,6 +22,7 @@ import io.openems.edge.common.modbusslave.ModbusSlave; import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.sum.GridMode; import io.openems.edge.common.taskmanager.Priority; import io.openems.edge.ess.api.ManagedSymmetricEss; import io.openems.edge.ess.api.SymmetricEss; @@ -57,11 +59,12 @@ public AbstractEssStreetscooter() { ); this.channel(SymmetricEss.ChannelId.MAX_APPARENT_POWER) .setNextValue(AbstractEssStreetscooter.MAX_APPARENT_POWER); + this.channel(SymmetricEss.ChannelId.GRID_MODE).setNextValue(GridMode.ON_GRID); this.powerHandler = new PowerHandler(this); } - protected void activate(ComponentContext context, String id, boolean enabled, boolean readonly, int unitId, + protected void activate(ComponentContext context, String id, String alias, boolean enabled, boolean readonly, int unitId, ConfigurationAdmin cm, String modbusReference, String modbusId) { this.readonly = readonly; @@ -70,7 +73,7 @@ protected void activate(ComponentContext context, String id, boolean enabled, bo this.getMaxApparentPower().setNextValue(0); } - super.activate(context, id, enabled, unitId, cm, modbusReference, modbusId); + super.activate(context, id, alias, enabled, unitId, cm, modbusReference, modbusId); } @Override @@ -282,12 +285,12 @@ public Constraint[] getStaticConstraints() { } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - ManagedSymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(AbstractEssStreetscooter.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(AbstractEssStreetscooter.class, accessMode, 300) // .build()); } } diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config0.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config0.java index 695a8b5190e..b6a5bc4d2fc 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config0.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config0.java @@ -7,8 +7,14 @@ name = "ESS Streetscooter 0", // description = "Implements the streetscooter energy storage system.") @interface Config0 { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Read-Only mode", description = "Enables Read-Only mode") diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config1.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config1.java index 318980b2dc8..610b740cd0d 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config1.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Config1.java @@ -7,8 +7,14 @@ name = "ESS Streetscooter 1", // description = "Implements the streetscooter energy storage system.") @interface Config1 { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess1"; + @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 = "Read-Only mode", description = "Enables Read-Only mode") diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess0Streetscooter.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess0Streetscooter.java index 4757536a40f..7fe88bb60fb 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess0Streetscooter.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess0Streetscooter.java @@ -48,8 +48,8 @@ public Ess0Streetscooter() { @Activate protected void activate(ComponentContext context, Config0 config0) { - super.activate(context, config0.id(), config0.enabled(), config0.readonly(), UNIT_ID, - this.cm, "Modbus", config0.modbus_id()); + super.activate(context, config0.id(), config0.alias(), config0.enabled(), config0.readonly(), UNIT_ID, this.cm, + "Modbus", config0.modbus_id()); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess1Streetscooter.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess1Streetscooter.java index 8a75ee3cfda..8e69498b74c 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess1Streetscooter.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/Ess1Streetscooter.java @@ -49,8 +49,8 @@ public Ess1Streetscooter() { @Activate protected void activate(ComponentContext context, Config1 config1) { - super.activate(context, config1.id(), config1.enabled(), config1.readonly(), UNIT_ID, this.cm, "Modbus", - config1.modbus_id()); + super.activate(context, config1.id(), config1.alias(), config1.enabled(), config1.readonly(), UNIT_ID, this.cm, + "Modbus", config1.modbus_id()); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/InverterMode.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/InverterMode.java index e44739355cb..669c50dc979 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/InverterMode.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/InverterMode.java @@ -1,6 +1,6 @@ package io.openems.edge.ess.streetscooter; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum InverterMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/PowerHandler.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/PowerHandler.java index 56c4da69449..f9d679a3a70 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/PowerHandler.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/PowerHandler.java @@ -6,7 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.channel.BooleanReadChannel; import io.openems.edge.common.channel.BooleanWriteChannel; import io.openems.edge.common.channel.EnumReadChannel; @@ -87,7 +87,7 @@ private void writeActivePower(Integer activePower) { try { IntegerWriteChannel setActivePowerChannel = parent.channel(StrtsctrChannelId.INVERTER_SET_ACTIVE_POWER); setActivePowerChannel.setNextWriteValue(activePower); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to set ActivePower: " + e.getMessage()); } } diff --git a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/StrtsctrChannelId.java b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/StrtsctrChannelId.java index 3ff72f31220..865b2626b4f 100644 --- a/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/StrtsctrChannelId.java +++ b/io.openems.edge.ess.streetscooter/src/io/openems/edge/ess/streetscooter/StrtsctrChannelId.java @@ -1,7 +1,8 @@ package io.openems.edge.ess.streetscooter; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.BooleanWriteChannel; import io.openems.edge.common.channel.ChannelId; @@ -9,7 +10,6 @@ import io.openems.edge.common.channel.IntegerDoc; import io.openems.edge.common.channel.IntegerReadChannel; import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.Unit; import io.openems.edge.ess.api.SymmetricEss; public enum StrtsctrChannelId implements ChannelId { @@ -26,8 +26,10 @@ public enum StrtsctrChannelId implements ChannelId { // BooleanWriteChannel ICU_RUN(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel(StrtsctrChannelId.DEBUG_ICU_RUN))), // ICU_ENABLED(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // .onInit(new BooleanWriteChannel.MirrorToDebugChannel(StrtsctrChannelId.DEBUG_ICU_ENABLED))), // // IntegerReadChannel @@ -121,6 +123,7 @@ public enum StrtsctrChannelId implements ChannelId { // IntegerWriteChannel INVERTER_SET_ACTIVE_POWER(new IntegerDoc() // + .accessMode(AccessMode.WRITE_ONLY) // .unit(Unit.WATT) // .onInit(new IntegerWriteChannel.MirrorToDebugChannel(StrtsctrChannelId.DEBUG_INVERTER_SET_ACTIVE_POWER))), // diff --git a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java index 37f909b5570..7bcf94a16dc 100644 --- a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java +++ b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java @@ -2,13 +2,15 @@ import org.osgi.annotation.versioning.ProviderType; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.channel.WriteChannel; import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; +import io.openems.edge.common.modbusslave.ModbusType; @ProviderType public interface Evcs extends OpenemsComponent { @@ -28,6 +30,37 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { CHARGE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // .accessMode(AccessMode.READ_ONLY)), // + + + /** + * Minimum Power . + * + *

      + *
    • Interface: Evcs + *
    • Writable + *
    • Type: Integer + *
    • Unit: W + *
    + */ + MINIMUM_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT) // + .accessMode(AccessMode.READ_WRITE)), + + + /** + * Maximum Power . + * + *
      + *
    • Interface: Evcs + *
    • Writable + *
    • Type: Integer + *
    • Unit: W + *
    + */ + MAXIMUM_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT) // + .accessMode(AccessMode.READ_WRITE)), + /** * Maximum Power valid by the Hardware. * @@ -66,7 +99,14 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ SET_DISPLAY_TEXT(Doc.of(OpenemsType.STRING) // - .accessMode(AccessMode.READ_WRITE)); + .accessMode(AccessMode.READ_WRITE)), + + SET_ENABLED(Doc.of(OpenemsType.BOOLEAN) // + .accessMode(AccessMode.WRITE_ONLY) // + .unit(Unit.ON_OFF) + .text("Disabled is indicated with a blue flashing LED. " + + "ATTENTION: Some electric vehicles (EVs) do not yet meet the standard requirements " + + "and disabling can lead to an error in the charging station.")); // private final Doc doc; @@ -79,6 +119,20 @@ public Doc doc() { return this.doc; } } + + + public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) { + return ModbusSlaveNatureTable.of(Evcs.class, accessMode, 100) // + .channel(0, ChannelId.CHARGE_POWER, ModbusType.UINT16) // + .channel(1, ChannelId.HARDWARE_POWER_LIMIT, ModbusType.UINT16) // + .channel(2, ChannelId.SET_CHARGE_POWER, ModbusType.UINT16) + .channel(3, ChannelId.SET_DISPLAY_TEXT, ModbusType.STRING16) + .channel(19,ChannelId.MINIMUM_POWER, ModbusType.UINT16) + .channel(20, ChannelId.MAXIMUM_POWER, ModbusType.UINT16) + .channel(21, ChannelId.SET_ENABLED, ModbusType.UINT16) + .build(); + } + /** * Gets the Charge Power in [W]. @@ -116,4 +170,16 @@ public default WriteChannel setChargePower() { public default WriteChannel setDisplayText() { return this.channel(ChannelId.SET_DISPLAY_TEXT); } + + /** + * Aktivates or deaktivates the Charging Station. + * ATTENTION: Some electric vehicles (EVs) do not yet meet the standard requirements + * and disabling can lead to an error in the charging station. + * + * @return the WriteChannel + */ + public default WriteChannel setEnabled() { + return this.channel(ChannelId.SET_ENABLED); + } + } diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Config.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Config.java index fd6d53e9567..8c08cb85582 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Config.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Config.java @@ -7,12 +7,18 @@ name = "EVCS KEBA KeContact", // description = "Implements the KEBA KeContact P20/P30 electric vehicle charging station.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "evcs0"; + @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 = "IP-Address", description = "The IP address of the charging station.") - String ip(); + String ip() default "192.168.25.11"; String webconsole_configurationFactory_nameHint() default "EVCS KEBA KeContact [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaChannelId.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaChannelId.java index 5156d8a6987..edc13938bd9 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaChannelId.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaChannelId.java @@ -1,10 +1,9 @@ package io.openems.edge.evcs.keba.kecontact; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; -import io.openems.edge.common.channel.AccessMode; public enum KebaChannelId implements io.openems.edge.common.channel.ChannelId { /* @@ -60,16 +59,6 @@ public enum KebaChannelId implements io.openems.edge.common.channel.ChannelId { PHASES(Doc.of(OpenemsType.INTEGER).text("Count of ladders, the car is louding with")), // - /* - * Write Channels - */ - SET_ENABLED(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.WRITE_ONLY) // - .unit(Unit.ON_OFF) - .text("Disabled is indicated with a blue flashing LED. " - + "ATTENTION: Some electric vehicles (EVs) do not yet meet the standard requirements " - + "and disabling can lead to an error in the charging station.")), // - /* * Fail State Channels */ diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaKeContact.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaKeContact.java index 5066cfc2100..6669dd48215 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaKeContact.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/KebaKeContact.java @@ -23,9 +23,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.common.channel.AccessMode; +import io.openems.edge.common.channel.Channel; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; +import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.modbusslave.ModbusType; import io.openems.edge.evcs.api.Evcs; import io.openems.edge.evcs.keba.kecontact.core.KebaKeContactCore; @@ -35,7 +41,8 @@ immediate = true, // configurationPolicy = ConfigurationPolicy.REQUIRE, // property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE) -public class KebaKeContact extends AbstractOpenemsComponent implements Evcs, OpenemsComponent, EventHandler { +public class KebaKeContact extends AbstractOpenemsComponent + implements Evcs, OpenemsComponent, EventHandler, ModbusSlave { public final static int UDP_PORT = 7090; @@ -43,10 +50,12 @@ public class KebaKeContact extends AbstractOpenemsComponent implements Evcs, Ope private final ReadWorker readWorker = new ReadWorker(this); private final ReadHandler readHandler = new ReadHandler(this); private final WriteHandler writeHandler = new WriteHandler(this); + private Boolean lastConnectionLostState = false; @Reference(policy = ReferencePolicy.STATIC, cardinality = ReferenceCardinality.MANDATORY) private KebaKeContactCore kebaKeContactCore = null; + public KebaKeContact() { super(// OpenemsComponent.ChannelId.values(), // @@ -59,7 +68,7 @@ public KebaKeContact() { @Activate void activate(ComponentContext context, Config config) throws UnknownHostException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); this.ip = Inet4Address.getByName(config.ip()); @@ -69,11 +78,13 @@ void activate(ComponentContext context, Config config) throws UnknownHostExcepti this.kebaKeContactCore.onReceive((ip, message) -> { if (ip.equals(this.ip)) { // same IP -> handle message this.readHandler.accept(message); + this.channel(KebaChannelId.ChargingStation_COMMUNICATION_FAILED).setNextValue(false); } }); // start queryWorker this.readWorker.activate(this.id() + "query"); + } @Deactivate @@ -86,6 +97,17 @@ protected void deactivate() { public void handleEvent(Event event) { switch (event.getTopic()) { case EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE: + + // Clear channels if the connection to the Charging Station has been lost + Channel connectionLostChannel = this.channel(KebaChannelId.ChargingStation_COMMUNICATION_FAILED); + Boolean connectionLost = connectionLostChannel.value().orElse(lastConnectionLostState); + if (connectionLost != lastConnectionLostState) { + if (connectionLost) { + resetChannelValues(); + } + lastConnectionLostState = connectionLost; + } + // handle writes this.writeHandler.run(); break; @@ -145,4 +167,71 @@ public ReadWorker getReadWorker() { public ReadHandler getReadHandler() { return readHandler; } + + /** + * Resets all channel values except + * the Communication_Failed channel + */ + private void resetChannelValues() { + for (KebaChannelId c : KebaChannelId.values()) { + if (c != KebaChannelId.ChargingStation_COMMUNICATION_FAILED) { + Channel channel = this.channel(c); + channel.setNextValue(null); + } + } + + } + + @Override + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { + + return new ModbusSlaveTable( + OpenemsComponent.getModbusSlaveNatureTable(accessMode), + Evcs.getModbusSlaveNatureTable(accessMode), + this.getModbusSlaveNatureTable(accessMode) + + ); + } + + private ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode accessMode) { + + return ModbusSlaveNatureTable.of(KebaKeContact.class, accessMode, 300) // + + + .channel(0, KebaChannelId.PRODUCT, ModbusType.STRING16) + .channel(16, KebaChannelId.SERIAL, ModbusType.STRING16) + .channel(32, KebaChannelId.FIRMWARE, ModbusType.STRING16) + .channel(48, KebaChannelId.COM_MODULE, ModbusType.STRING16) + .channel(64, KebaChannelId.STATUS, ModbusType.UINT16) + .channel(65, KebaChannelId.ERROR_1, ModbusType.UINT16) + .channel(66, KebaChannelId.ERROR_2, ModbusType.UINT16) + .channel(67, KebaChannelId.PLUG, ModbusType.UINT16) + .channel(68, KebaChannelId.ENABLE_SYS, ModbusType.UINT16) + .channel(69, KebaChannelId.ENABLE_USER, ModbusType.UINT16) + .channel(70, KebaChannelId.MAX_CURR_PERCENT, ModbusType.UINT16) + .channel(71, KebaChannelId.CURR_USER, ModbusType.UINT16) + .channel(72, KebaChannelId.CURR_FAILSAFE, ModbusType.UINT16) + .channel(73, KebaChannelId.TIMEOUT_FAILSAFE, ModbusType.UINT16) + .channel(74, KebaChannelId.CURR_TIMER, ModbusType.UINT16) + .channel(75, KebaChannelId.TIMEOUT_CT, ModbusType.UINT16) + .channel(76, KebaChannelId.ENERGY_LIMIT, ModbusType.UINT16) + .channel(77, KebaChannelId.OUTPUT, ModbusType.UINT16) + .channel(78, KebaChannelId.INPUT, ModbusType.UINT16) + + //Report 3 + .channel(79, KebaChannelId.VOLTAGE_L1, ModbusType.UINT16) + .channel(80, KebaChannelId.VOLTAGE_L2, ModbusType.UINT16) + .channel(81, KebaChannelId.VOLTAGE_L3, ModbusType.UINT16) + .channel(82, KebaChannelId.CURRENT_L1, ModbusType.UINT16) + .channel(83, KebaChannelId.CURRENT_L2, ModbusType.UINT16) + .channel(84, KebaChannelId.CURRENT_L3, ModbusType.UINT16) + .channel(85, KebaChannelId.ACTUAL_POWER, ModbusType.UINT16) + .channel(86, KebaChannelId.COS_PHI, ModbusType.UINT16) + .channel(87, KebaChannelId.ENERGY_SESSION, ModbusType.UINT16) + .channel(88, KebaChannelId.ENERGY_TOTAL, ModbusType.UINT16) + .channel(89, KebaChannelId.PHASES, ModbusType.UINT16) + .uint16Reserved(90) + .channel(91, KebaChannelId.ChargingStation_COMMUNICATION_FAILED, ModbusType.UINT16) + .build(); + } } diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Plug.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Plug.java index dc9c51cef10..34d70c21d74 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Plug.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Plug.java @@ -1,6 +1,6 @@ package io.openems.edge.evcs.keba.kecontact; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum Plug implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadHandler.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadHandler.java index d9ffcb67c79..c0ae6825b30 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadHandler.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadHandler.java @@ -122,12 +122,13 @@ public void accept(String message) { Channel currentL2 = parent.channel(KebaChannelId.CURRENT_L2); Channel currentL3 = parent.channel(KebaChannelId.CURRENT_L3); - if (currentL1.value().orElse(0) > 0){ + if (currentL1.value().orElse(0) > 10){ if (currentL3.value().orElse(0) > 100) { this.parent.logInfo(this.log, "KEBA is loading on three ladder"); set(KebaChannelId.PHASES, 3); + } else if (currentL2.value().orElse(0) > 100) { this.parent.logInfo(this.log, "KEBA is loading on two ladder"); set(KebaChannelId.PHASES, 2); @@ -136,7 +137,20 @@ public void accept(String message) { this.parent.logInfo(this.log, "KEBA is loading on one ladder"); set(KebaChannelId.PHASES, 1); } + Channel phases = this.parent.channel(KebaChannelId.PHASES); + this.parent.channel(Evcs.ChannelId.MINIMUM_POWER).setNextValue(230 /*Spannung*/ * 6 /*min Strom*/ * phases.value().orElse(3)); + this.parent.channel(Evcs.ChannelId.MAXIMUM_POWER).setNextValue(230 /*Spannung*/ * 32 /*max Strom*/ * phases.value().orElse(3)); + }else { + + // set Min & Max Power to Default values that allows the User a power setting between those values + Channel min = this.parent.channel(Evcs.ChannelId.MINIMUM_POWER); + Channel max = this.parent.channel(Evcs.ChannelId.MAXIMUM_POWER); + if(min.value().orElse(null)==null || max.value().orElse(null) == null) { + this.parent.channel(Evcs.ChannelId.MINIMUM_POWER).setNextValue(230 /*Spannung*/ * 6 /*min Strom*/ * 3); + this.parent.channel(Evcs.ChannelId.MAXIMUM_POWER).setNextValue(230 /*Spannung*/ * 32 /*max Strom*/ * 3); + } } + // Set CHARGE_POWER Optional power_mw = JsonUtils.getAsOptionalInt(jMessage, "P"); // in [mW] @@ -163,9 +177,6 @@ public void accept(String message) { if (jMessage.has("Enable sys")) { setBoolean(KebaChannelId.ENABLE_SYS, jMessage, "Enable sys"); } - if (jMessage.has("Max curr")) { - setInt(KebaChannelId.MAX_CURR, jMessage, "Max curr"); - } if (jMessage.has("E pres")) { setInt(KebaChannelId.ENERGY_SESSION, jMessage, "E pres"); } @@ -194,17 +205,17 @@ private void setBoolean(KebaChannelId channelId, JsonObject jMessage, String nam } } - /** - * returns true or false, if the requested report answered or not + * returns true or false, if the requested report answered or not + * and set that value to false * * @param report * @return */ public boolean hasResultandReset(Report report) { - + boolean result = false; - switch(report) { + switch (report) { case REPORT1: result = receiveReport1; receiveReport1 = false; diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadWorker.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadWorker.java index 647962bb424..669293c2ca1 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadWorker.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/ReadWorker.java @@ -3,15 +3,10 @@ import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import io.openems.common.worker.AbstractWorker; public class ReadWorker extends AbstractWorker { - private final Logger log = LoggerFactory.getLogger(KebaKeContact.class); - private final KebaKeContact parent; private LocalDateTime lastReport1 = LocalDateTime.MIN; @@ -119,7 +114,6 @@ public void triggerNextRun() { * @param receivedAMessage return value from the ReadHandler */ private void currentCommunication(boolean receivedAMessage) { - this.parent.logInfo(log, "Existing charging communication: "+receivedAMessage); this.parent.channel(KebaChannelId.ChargingStation_COMMUNICATION_FAILED).setNextValue(!receivedAMessage); } diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Status.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Status.java index 84ddff07822..7ce087d73be 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Status.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/Status.java @@ -1,6 +1,6 @@ package io.openems.edge.evcs.keba.kecontact; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum Status implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/WriteHandler.java b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/WriteHandler.java index 70fe2f8b63b..8dddd94e2e1 100644 --- a/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/WriteHandler.java +++ b/io.openems.edge.evcs.keba.kecontact/src/io/openems/edge/evcs/keba/kecontact/WriteHandler.java @@ -6,7 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.WriteChannel; import io.openems.edge.evcs.api.Evcs; @@ -23,7 +23,8 @@ public class WriteHandler implements Runnable { /** * Minimum pause between two consecutive writes */ - private final static int WRITE_INTERVAL_SECONDS = 60; // 60 seconds + private final static int WRITE_INTERVAL_SECONDS = 5; // before change the interval was 60 seconds + private final static int WRITE_DISPLAY_INTERVAL_SECONDS = 60; public WriteHandler(KebaKeContact parent) { this.parent = parent; @@ -64,7 +65,7 @@ private void setDisplay() { this.parent.logInfo(this.log, "Setting KEBA KeContact display text to [" + display + "]"); boolean sentSuccessfully = parent.send("display 0 0 0 0 " + display); if (sentSuccessfully) { - this.nextDisplayWrite = LocalDateTime.now().plusSeconds(WRITE_INTERVAL_SECONDS); + this.nextDisplayWrite = LocalDateTime.now().plusSeconds(WRITE_DISPLAY_INTERVAL_SECONDS); this.lastDisplay = display; } } @@ -75,10 +76,14 @@ private void setDisplay() { private LocalDateTime nextEnabledWrite = LocalDateTime.MIN; /** + * SetEnabled is never used because it causes several errors that doesn't make sense + * If the charging station will be activated, all seems to work, but no charging is possible. + * + * * Sets the enabled state from SET_ENABLED channel */ private void setEnabled() { - WriteChannel channel = this.parent.channel(KebaChannelId.SET_ENABLED); + WriteChannel channel = this.parent.channel(Evcs.ChannelId.SET_ENABLED); Optional valueOpt = channel.getNextWriteValueAndReset(); if (valueOpt.isPresent()) { Boolean enabled = valueOpt.get(); @@ -105,6 +110,10 @@ private void setEnabled() { * the DIP-switch settings and the used cable of the charging station. */ private void setPower() { + if(this.nextEnabledWrite.isAfter(LocalDateTime.now())) { + return; + } + WriteChannel channel = this.parent.channel(Evcs.ChannelId.SET_CHARGE_POWER); Optional valueOpt = channel.getNextWriteValueAndReset(); if (valueOpt.isPresent()) { @@ -113,8 +122,12 @@ private void setPower() { Channel phases = this.parent.channel(KebaChannelId.PHASES); Integer current = power * 1000 / phases.value().orElse(3) /* e.g. 3 phases */ / 230 /* voltage */ ; + // limits the charging value because KEBA knows only values between 6000 and 63000 + if(current>63000) { + current = 63000; + } + if (!current.equals(this.lastCurrent) || this.nextCurrentWrite.isBefore(LocalDateTime.now())) { - this.parent.logInfo(this.log, "Setting KEBA KeContact current to [" + current + " A] - calculated from [" + power + " W] by " + phases.value().orElse(3) + " Phase"); @@ -122,7 +135,7 @@ private void setPower() { Channel currPower = this.parent.channel(KebaChannelId.ACTUAL_POWER); this.parent.setDisplayText() .setNextWriteValue("Charging " + (currPower.value().orElse(0) / 1000) + "W"); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { e.printStackTrace(); } diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config1.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config1.java index 3f044fd772b..d02c231a7ae 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config1.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config1.java @@ -8,8 +8,13 @@ description = "The MPP tracker 1 implementation of a FENECON DESS (PRO Hybrid, PRO Compact,...)") @interface Config1 { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "charger0"; + @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config2.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config2.java index ba23325fa34..92f8406a6d7 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config2.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/Config2.java @@ -8,8 +8,13 @@ description = "The MPP tracker 2 implementation of a FENECON DESS (PRO Hybrid, PRO Compact,...)") @interface Config2 { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "charger1"; + @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger1.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger1.java index 5535bf1888c..5770d259abc 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger1.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger1.java @@ -38,8 +38,8 @@ public FeneconDessCharger1() { @Activate void activate(ComponentContext context, Config1 config) { - super.activate(context, config.id(), config.enabled(), FeneconDessConstants.UNIT_ID, - this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); } @Deactivate diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger2.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger2.java index fe9c59dfd3e..7018484c4c1 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger2.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/FeneconDessCharger2.java @@ -38,8 +38,8 @@ public FeneconDessCharger2() { @Activate void activate(ComponentContext context, Config2 config) { - super.activate(context, config.id(), config.enabled(), FeneconDessConstants.UNIT_ID, - this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); } @Deactivate diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/BsmuWorkState.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/BsmuWorkState.java index ba6975fa1c3..3f8677b0b08 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/BsmuWorkState.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/BsmuWorkState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.dess.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BsmuWorkState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/Config.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/Config.java index 82c2aada60d..d1791f8aacd 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/Config.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/Config.java @@ -8,8 +8,13 @@ description = "The energy storage system implementation of a FENECON DESS (PRO Hybrid, PRO Compact,...)") @interface Config { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEss.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEss.java index 733bc2f106b..a05adc131fd 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEss.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEss.java @@ -66,8 +66,8 @@ public FeneconDessEss() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, "Modbus", - config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); } @Deactivate diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/StackChargeState.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/StackChargeState.java index 34ae4c97159..52646a8b801 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/StackChargeState.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/StackChargeState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.dess.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum StackChargeState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/SystemState.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/SystemState.java index 49b7f25fc68..6cc3ba7031d 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/SystemState.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/SystemState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.dess.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SystemState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/Config.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/Config.java index 46c8b7fbda6..828e98813b3 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/Config.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/Config.java @@ -8,8 +8,13 @@ description = "The grid-meter implementation of a FENECON DESS (PRO Hybrid, PRO Compact,...)") @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeter.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeter.java index d6d7cf23617..a326ea96cc9 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeter.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeter.java @@ -56,8 +56,8 @@ public FeneconDessGridMeter() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, "Modbus", - config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); } @Deactivate diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/Config.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/Config.java index 90bed82ad0f..442a0e2a72e 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/Config.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/Config.java @@ -7,8 +7,14 @@ name = "FENECON DESS PV-Meter", // description = "The pv-meter implementation of a FENECON DESS.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter1"; + @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeter.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeter.java index f7c2d1d1b34..567b94ab4da 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeter.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeter.java @@ -72,8 +72,8 @@ public FeneconDessPvMeter() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, "Modbus", - config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconDessConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); this.modbusBridgeId = config.modbus_id(); } diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/BatteryGroupState.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/BatteryGroupState.java index 98ad8732b4d..91a430ab991 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/BatteryGroupState.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/BatteryGroupState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.mini.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BatteryGroupState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/Config.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/Config.java index a19af951362..ab01c358122 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/Config.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/Config.java @@ -10,8 +10,13 @@ description = "The energy storage system implementation of a FENECON Mini.") @interface Config { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Phase", description = "On which Phase is the Mini connected?") diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/ControlMode.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/ControlMode.java index cc8acf1cf45..f652937a03b 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/ControlMode.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/ControlMode.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.mini.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ControlMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/EssChannelId.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/EssChannelId.java index 6e8ede7a00b..85e20e08f45 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/EssChannelId.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/EssChannelId.java @@ -1,10 +1,10 @@ package io.openems.edge.fenecon.mini.ess; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; public enum EssChannelId implements io.openems.edge.common.channel.ChannelId { // EnumReadChannels @@ -48,29 +48,32 @@ public enum EssChannelId implements io.openems.edge.common.channel.ChannelId { BATTERY_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT)), // - BECU1_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), // - BECU1_DISCHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), // - BECU1_VOLT(Doc.of(OpenemsType.INTEGER) // + BECU1_CHARGE_CURRENT_LIMIT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.MILLIAMPERE)), // + BECU1_DISCHARGE_CURRENT_LIMIT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.MILLIAMPERE)), // + BECU1_TOTAL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)), // - BECU1_CURRENT(Doc.of(OpenemsType.INTEGER) // + BECU1_TOTAL_CURRENT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE)), // BECU1_SOC(Doc.of(OpenemsType.INTEGER) // .unit(Unit.PERCENT)), // - BECU1_VERSION(Doc.of(OpenemsType.INTEGER)), // - BECU1_MIN_VOLT_NO(Doc.of(OpenemsType.INTEGER)), // - BECU1_MIN_VOLT(Doc.of(OpenemsType.INTEGER) // + BECU1_NOMINAL_CAPACITY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE_HOURS)), // + BECU1_CURRENT_CAPACITY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE_HOURS)), // + BECU1_MINIMUM_VOLTAGE_NO(Doc.of(OpenemsType.INTEGER)), // + BECU1_MINIMUM_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)), // - BECU1_MAX_VOLT_NO(Doc.of(OpenemsType.INTEGER)), // - BECU1_MAX_VOLT(Doc.of(OpenemsType.INTEGER) // + BECU1_MAXIMUM_VOLTAGE_NO(Doc.of(OpenemsType.INTEGER)), // + BECU1_MAXIMUM_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)), // - BECU1_MIN_TEMP_NO(Doc.of(OpenemsType.INTEGER)), // - BECU1_MIN_TEMP(Doc.of(OpenemsType.INTEGER) // + BECU1_MINIMUM_TEMPERATURE_NO(Doc.of(OpenemsType.INTEGER)), // + BECU1_MINIMUM_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.DEGREE_CELSIUS)), // - BECU1_MAX_TEMP_NO(Doc.of(OpenemsType.INTEGER)), // - BECU1_MAX_TEMP(Doc.of(OpenemsType.INTEGER) // + BECU1_MAXIMUM_TEMPERATURE_NO(Doc.of(OpenemsType.INTEGER)), // + BECU1_MAXIMUM_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.DEGREE_CELSIUS)), // BECU2_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEss.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEss.java index eb04e9640a8..f25f71ee8ab 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEss.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEss.java @@ -12,16 +12,20 @@ import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.AccessMode; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; +import io.openems.edge.bridge.modbus.api.ElementToChannelOffsetConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; import io.openems.edge.bridge.modbus.api.element.SignedWordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; +import io.openems.edge.common.channel.IntegerReadChannel; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.modbusslave.ModbusSlave; import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; @@ -63,8 +67,8 @@ public FeneconMiniEss() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), FeneconMiniConstants.UNIT_ID, this.cm, "Modbus", - config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconMiniConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); this.phase = config.phase(); SinglePhaseEss.initializeCopyPhaseChannel(this, config.phase()); } @@ -103,82 +107,83 @@ protected ModbusProtocol defineModbusProtocol() { m(AsymmetricEss.ChannelId.ACTIVE_POWER_L3, new UnsignedWordElement(2207), UNSIGNED_POWER_CONVERTER)), // new FC3ReadRegistersTask(3000, Priority.LOW, // - m(EssChannelId.BECU1_CHARGE_CURRENT, new UnsignedWordElement(3000)), // - m(EssChannelId.BECU1_DISCHARGE_CURRENT, new UnsignedWordElement(3001)), // - m(EssChannelId.BECU1_VOLT, new UnsignedWordElement(3002)), // - m(EssChannelId.BECU1_CURRENT, new UnsignedWordElement(3003)), // - m(EssChannelId.BECU1_SOC, new UnsignedWordElement(3004))), // - new FC3ReadRegistersTask(3005, Priority.LOW, // - bm(new UnsignedWordElement(3005))// - .m(EssChannelId.STATE_1, 0)// - .m(EssChannelId.STATE_2, 1)// - .m(EssChannelId.STATE_3, 2)// - .m(EssChannelId.STATE_4, 3)// - .m(EssChannelId.STATE_5, 4)// - .m(EssChannelId.STATE_6, 5)// - .m(EssChannelId.STATE_7, 6)// - .m(EssChannelId.STATE_8, 7)// - .m(EssChannelId.STATE_9, 8)// - .m(EssChannelId.STATE_10, 9)// - .m(EssChannelId.STATE_11, 10)// - .m(EssChannelId.STATE_12, 11)// - .m(EssChannelId.STATE_13, 12)// - .m(EssChannelId.STATE_14, 13)// - .m(EssChannelId.STATE_15, 14)// - .m(EssChannelId.STATE_16, 15)// - .build(), // - bm(new UnsignedWordElement(3006))// - .m(EssChannelId.STATE_17, 0)// - .m(EssChannelId.STATE_18, 1)// - .m(EssChannelId.STATE_19, 2)// - .m(EssChannelId.STATE_20, 4)// - .m(EssChannelId.STATE_21, 5)// - .m(EssChannelId.STATE_22, 6)// - .m(EssChannelId.STATE_23, 7)// - .m(EssChannelId.STATE_24, 8)// - .m(EssChannelId.STATE_25, 9)// - .m(EssChannelId.STATE_26, 10)// - .m(EssChannelId.STATE_27, 11)// - .m(EssChannelId.STATE_28, 12)// - .m(EssChannelId.STATE_29, 13)// - .m(EssChannelId.STATE_30, 14)// - .m(EssChannelId.STATE_31, 15)// - .build(), // - bm(new UnsignedWordElement(3007))// - .m(EssChannelId.STATE_32, 0)// - .m(EssChannelId.STATE_33, 1)// - .m(EssChannelId.STATE_34, 2)// - .m(EssChannelId.STATE_35, 3)// - .m(EssChannelId.STATE_36, 4)// - .m(EssChannelId.STATE_37, 5)// - .m(EssChannelId.STATE_38, 6)// - .m(EssChannelId.STATE_39, 7)// - .m(EssChannelId.STATE_40, 8)// - .m(EssChannelId.STATE_41, 9)// - .m(EssChannelId.STATE_42, 10)// - .m(EssChannelId.STATE_43, 13)// - .m(EssChannelId.STATE_44, 14)// - .m(EssChannelId.STATE_45, 15)// - .build(), // - bm(new UnsignedWordElement(3008))// - .m(EssChannelId.STATE_46, 0)// - .m(EssChannelId.STATE_47, 1)// - .m(EssChannelId.STATE_48, 2)// - .m(EssChannelId.STATE_49, 9)// - .m(EssChannelId.STATE_50, 10)// - .m(EssChannelId.STATE_51, 12)// - .m(EssChannelId.STATE_52, 13)// - .build(), // + m(EssChannelId.BECU1_CHARGE_CURRENT_LIMIT, new UnsignedWordElement(3000), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(EssChannelId.BECU1_DISCHARGE_CURRENT_LIMIT, new UnsignedWordElement(3001), // + ElementToChannelConverter.SCALE_FACTOR_2), // + m(EssChannelId.BECU1_TOTAL_VOLTAGE, new UnsignedWordElement(3002), + ElementToChannelConverter.SCALE_FACTOR_2), // + m(EssChannelId.BECU1_TOTAL_CURRENT, new UnsignedWordElement(3003)), // + m(EssChannelId.BECU1_SOC, new UnsignedWordElement(3004)), // + m(new BitsWordElement(3005, this) // + .bit(0, EssChannelId.STATE_1) // + .bit(1, EssChannelId.STATE_2) // + .bit(2, EssChannelId.STATE_3) // + .bit(3, EssChannelId.STATE_4) // + .bit(4, EssChannelId.STATE_5) // + .bit(5, EssChannelId.STATE_6) // + .bit(6, EssChannelId.STATE_7) // + .bit(7, EssChannelId.STATE_8) // + .bit(8, EssChannelId.STATE_9) // + .bit(9, EssChannelId.STATE_10) // + .bit(10, EssChannelId.STATE_11) // + .bit(11, EssChannelId.STATE_12) // + .bit(12, EssChannelId.STATE_13) // + .bit(13, EssChannelId.STATE_14) // + .bit(14, EssChannelId.STATE_15) // + .bit(15, EssChannelId.STATE_16)), // + m(new BitsWordElement(3006, this) // + .bit(0, EssChannelId.STATE_17) // + .bit(1, EssChannelId.STATE_18) // + .bit(2, EssChannelId.STATE_19) // + .bit(4, EssChannelId.STATE_20) // + .bit(5, EssChannelId.STATE_21) // + .bit(6, EssChannelId.STATE_22) // + .bit(7, EssChannelId.STATE_23) // + .bit(8, EssChannelId.STATE_24) // + .bit(9, EssChannelId.STATE_25) // + .bit(10, EssChannelId.STATE_26) // + .bit(11, EssChannelId.STATE_27) // + .bit(12, EssChannelId.STATE_28) // + .bit(13, EssChannelId.STATE_29) // + .bit(14, EssChannelId.STATE_30) // + .bit(15, EssChannelId.STATE_31)), // + m(new BitsWordElement(3007, this) // + .bit(0, EssChannelId.STATE_32) // + .bit(1, EssChannelId.STATE_33) // + .bit(2, EssChannelId.STATE_34) // + .bit(3, EssChannelId.STATE_35) // + .bit(4, EssChannelId.STATE_36) // + .bit(5, EssChannelId.STATE_37) // + .bit(6, EssChannelId.STATE_38) // + .bit(7, EssChannelId.STATE_39) // + .bit(8, EssChannelId.STATE_40) // + .bit(9, EssChannelId.STATE_41) // + .bit(10, EssChannelId.STATE_42) // + .bit(13, EssChannelId.STATE_43) // + .bit(14, EssChannelId.STATE_44) // + .bit(15, EssChannelId.STATE_45)), // + m(new BitsWordElement(3008, this) // + .bit(0, EssChannelId.STATE_46) // + .bit(1, EssChannelId.STATE_47) // + .bit(2, EssChannelId.STATE_48) // + .bit(9, EssChannelId.STATE_49) // + .bit(10, EssChannelId.STATE_50) // + .bit(12, EssChannelId.STATE_51) // + .bit(13, EssChannelId.STATE_52)), // m(EssChannelId.BECU1_VERSION, new UnsignedWordElement(3009)), // - new DummyRegisterElement(3010, 3011), // - m(EssChannelId.BECU1_MIN_VOLT_NO, new UnsignedWordElement(3012)), // - m(EssChannelId.BECU1_MIN_VOLT, new UnsignedWordElement(3013)), // - m(EssChannelId.BECU1_MAX_VOLT_NO, new UnsignedWordElement(3014)), // - m(EssChannelId.BECU1_MAX_VOLT, new UnsignedWordElement(3015)), // ^ - m(EssChannelId.BECU1_MIN_TEMP_NO, new UnsignedWordElement(3016)), // - m(EssChannelId.BECU1_MIN_TEMP, new UnsignedWordElement(3017)), // - m(EssChannelId.BECU1_MAX_TEMP_NO, new UnsignedWordElement(3018)), // - m(EssChannelId.BECU1_MAX_TEMP, new UnsignedWordElement(3019))), // + m(EssChannelId.BECU1_NOMINAL_CAPACITY, new UnsignedWordElement(3010)), // + m(EssChannelId.BECU1_CURRENT_CAPACITY, new UnsignedWordElement(3011)), // + m(EssChannelId.BECU1_MINIMUM_VOLTAGE_NO, new UnsignedWordElement(3012)), // + m(EssChannelId.BECU1_MINIMUM_VOLTAGE, new UnsignedWordElement(3013)), // + m(EssChannelId.BECU1_MAXIMUM_VOLTAGE_NO, new UnsignedWordElement(3014)), // + m(EssChannelId.BECU1_MAXIMUM_VOLTAGE, new UnsignedWordElement(3015)), // ^ + m(EssChannelId.BECU1_MINIMUM_TEMPERATURE_NO, new UnsignedWordElement(3016)), // + m(EssChannelId.BECU1_MINIMUM_TEMPERATURE, new UnsignedWordElement(3017), + new ElementToChannelOffsetConverter(-40)), // + m(EssChannelId.BECU1_MAXIMUM_TEMPERATURE_NO, new UnsignedWordElement(3018)), // + m(EssChannelId.BECU1_MAXIMUM_TEMPERATURE, new UnsignedWordElement(3019), + new ElementToChannelOffsetConverter(-40))), new FC3ReadRegistersTask(3200, Priority.LOW, // m(EssChannelId.BECU2_CHARGE_CURRENT, new UnsignedWordElement(3200)), // @@ -187,66 +192,62 @@ protected ModbusProtocol defineModbusProtocol() { m(EssChannelId.BECU2_CURRENT, new UnsignedWordElement(3203)), // m(EssChannelId.BECU2_SOC, new UnsignedWordElement(3204))), // new FC3ReadRegistersTask(3205, Priority.LOW, // - bm(new UnsignedWordElement(3205))// - .m(EssChannelId.STATE_53, 0)// - .m(EssChannelId.STATE_54, 1)// - .m(EssChannelId.STATE_55, 2)// - .m(EssChannelId.STATE_56, 3)// - .m(EssChannelId.STATE_57, 4)// - .m(EssChannelId.STATE_58, 5)// - .m(EssChannelId.STATE_59, 6)// - .m(EssChannelId.STATE_60, 7)// - .m(EssChannelId.STATE_61, 8)// - .m(EssChannelId.STATE_62, 9)// - .m(EssChannelId.STATE_63, 10)// - .m(EssChannelId.STATE_64, 11)// - .m(EssChannelId.STATE_65, 12)// - .m(EssChannelId.STATE_66, 13)// - .m(EssChannelId.STATE_67, 14)// - .m(EssChannelId.STATE_68, 15)// - .build(), // - bm(new UnsignedWordElement(3206))// - .m(EssChannelId.STATE_69, 0)// - .m(EssChannelId.STATE_70, 1)// - .m(EssChannelId.STATE_71, 2)// - .m(EssChannelId.STATE_72, 4)// - .m(EssChannelId.STATE_73, 5)// - .m(EssChannelId.STATE_74, 6)// - .m(EssChannelId.STATE_75, 7)// - .m(EssChannelId.STATE_76, 8)// - .m(EssChannelId.STATE_77, 9)// - .m(EssChannelId.STATE_78, 10)// - .m(EssChannelId.STATE_79, 11)// - .m(EssChannelId.STATE_80, 12)// - .m(EssChannelId.STATE_81, 13)// - .m(EssChannelId.STATE_82, 14)// - .m(EssChannelId.STATE_83, 15)// - .build(), // - bm(new UnsignedWordElement(3207))// - .m(EssChannelId.STATE_84, 0)// - .m(EssChannelId.STATE_85, 1)// - .m(EssChannelId.STATE_86, 2)// - .m(EssChannelId.STATE_87, 3)// - .m(EssChannelId.STATE_88, 4)// - .m(EssChannelId.STATE_89, 5)// - .m(EssChannelId.STATE_90, 6)// - .m(EssChannelId.STATE_91, 7)// - .m(EssChannelId.STATE_92, 8)// - .m(EssChannelId.STATE_93, 9)// - .m(EssChannelId.STATE_94, 10)// - .m(EssChannelId.STATE_95, 13)// - .m(EssChannelId.STATE_96, 14)// - .m(EssChannelId.STATE_97, 15)// - .build(), // - bm(new UnsignedWordElement(3208))// - .m(EssChannelId.STATE_98, 0)// - .m(EssChannelId.STATE_99, 1)// - .m(EssChannelId.STATE_100, 2)// - .m(EssChannelId.STATE_101, 9)// - .m(EssChannelId.STATE_102, 10)// - .m(EssChannelId.STATE_103, 12)// - .m(EssChannelId.STATE_104, 13)// - .build(), // + m(new BitsWordElement(3205, this) // + .bit(0, EssChannelId.STATE_53) // + .bit(1, EssChannelId.STATE_54) // + .bit(2, EssChannelId.STATE_55) // + .bit(3, EssChannelId.STATE_56) // + .bit(4, EssChannelId.STATE_57) // + .bit(5, EssChannelId.STATE_58) // + .bit(6, EssChannelId.STATE_59) // + .bit(7, EssChannelId.STATE_60) // + .bit(8, EssChannelId.STATE_61) // + .bit(9, EssChannelId.STATE_62) // + .bit(10, EssChannelId.STATE_63) // + .bit(11, EssChannelId.STATE_64) // + .bit(12, EssChannelId.STATE_65) // + .bit(13, EssChannelId.STATE_66) // + .bit(14, EssChannelId.STATE_67) // + .bit(15, EssChannelId.STATE_68)), // + m(new BitsWordElement(3206, this) // + .bit(0, EssChannelId.STATE_69) // + .bit(1, EssChannelId.STATE_70) // + .bit(2, EssChannelId.STATE_71) // + .bit(4, EssChannelId.STATE_72) // + .bit(5, EssChannelId.STATE_73) // + .bit(6, EssChannelId.STATE_74) // + .bit(7, EssChannelId.STATE_75) // + .bit(8, EssChannelId.STATE_76) // + .bit(9, EssChannelId.STATE_77) // + .bit(10, EssChannelId.STATE_78) // + .bit(11, EssChannelId.STATE_79) // + .bit(12, EssChannelId.STATE_80) // + .bit(13, EssChannelId.STATE_81) // + .bit(14, EssChannelId.STATE_82) // + .bit(15, EssChannelId.STATE_83)), + m(new BitsWordElement(3207, this) // + .bit(0, EssChannelId.STATE_84) // + .bit(1, EssChannelId.STATE_85) // + .bit(2, EssChannelId.STATE_86) // + .bit(3, EssChannelId.STATE_87) // + .bit(4, EssChannelId.STATE_88) // + .bit(5, EssChannelId.STATE_89) // + .bit(6, EssChannelId.STATE_90) // + .bit(7, EssChannelId.STATE_91) // + .bit(8, EssChannelId.STATE_92) // + .bit(9, EssChannelId.STATE_93) // + .bit(10, EssChannelId.STATE_94) // + .bit(13, EssChannelId.STATE_95) // + .bit(14, EssChannelId.STATE_96) // + .bit(15, EssChannelId.STATE_97)), // + m(new BitsWordElement(3208, this) // + .bit(0, EssChannelId.STATE_98) // + .bit(1, EssChannelId.STATE_99) // + .bit(2, EssChannelId.STATE_100) // + .bit(9, EssChannelId.STATE_101) // + .bit(10, EssChannelId.STATE_102) // + .bit(12, EssChannelId.STATE_103) // + .bit(13, EssChannelId.STATE_104)), m(EssChannelId.BECU2_VERSION, new UnsignedWordElement(3209)), // new DummyRegisterElement(3210, 3211), // m(EssChannelId.BECU2_MIN_VOLT_NO, new UnsignedWordElement(3212)), // @@ -269,56 +270,77 @@ protected ModbusProtocol defineModbusProtocol() { m(EssChannelId.BECU_DISCHARGE_CURRENT, new UnsignedWordElement(4804)), // m(EssChannelId.BECU_VOLT, new UnsignedWordElement(4805)), // m(EssChannelId.BECU_CURRENT, new UnsignedWordElement(4806))), // + new FC16WriteRegistersTask(4809, // + m(new BitsWordElement(4809, this) // + .bit(0, EssChannelId.STATE_111) // + .bit(1, EssChannelId.STATE_112))), // new FC3ReadRegistersTask(4808, Priority.LOW, // - bm(new UnsignedWordElement(4808))// - .m(EssChannelId.STATE_105, 0)// - .m(EssChannelId.STATE_106, 1)// - .m(EssChannelId.STATE_107, 2)// - .m(EssChannelId.STATE_108, 3)// - .m(EssChannelId.STATE_109, 4)// - .m(EssChannelId.STATE_110, 9)// - .build(), // - bm(new UnsignedWordElement(4809))// - .m(EssChannelId.STATE_111, 0)// - .m(EssChannelId.STATE_112, 1)// - .build(), // - bm(new UnsignedWordElement(4810))// - .m(EssChannelId.STATE_113, 0)// - .m(EssChannelId.STATE_114, 1)// - .m(EssChannelId.STATE_115, 2)// - .m(EssChannelId.STATE_116, 3)// - .m(EssChannelId.STATE_117, 4)// - .m(EssChannelId.STATE_118, 5)// - .m(EssChannelId.STATE_119, 6)// - .m(EssChannelId.STATE_120, 7)// - .m(EssChannelId.STATE_121, 8)// - .m(EssChannelId.STATE_122, 9)// - .m(EssChannelId.STATE_123, 10)// - .m(EssChannelId.STATE_124, 11)// - .m(EssChannelId.STATE_125, 12)// - .m(EssChannelId.STATE_126, 13)// - .m(EssChannelId.STATE_127, 14)// - .m(EssChannelId.STATE_128, 15)// - .build(), // - bm(new UnsignedWordElement(4811))// - .m(EssChannelId.STATE_129, 0)// - .m(EssChannelId.STATE_130, 1)// - .m(EssChannelId.STATE_131, 2)// - .m(EssChannelId.STATE_132, 3)// - .m(EssChannelId.STATE_133, 4)// - .m(EssChannelId.STATE_134, 5)// - .m(EssChannelId.STATE_135, 6)// - .m(EssChannelId.STATE_136, 7)// - .m(EssChannelId.STATE_137, 8)// - .m(EssChannelId.STATE_138, 9)// - .m(EssChannelId.STATE_139, 10)// - .m(EssChannelId.STATE_140, 11)// - .m(EssChannelId.STATE_141, 12)// - .m(EssChannelId.STATE_142, 13)// - .m(EssChannelId.STATE_143, 14)// - .build(), - - m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(4812))// + m(new BitsWordElement(4808, this) // + .bit(0, EssChannelId.STATE_105) // + .bit(1, EssChannelId.STATE_106) // + .bit(2, EssChannelId.STATE_107) // + .bit(3, EssChannelId.STATE_108) // + .bit(4, EssChannelId.STATE_109) // + .bit(9, EssChannelId.STATE_110)), // + m(new BitsWordElement(4809, this) // + .bit(0, EssChannelId.STATE_111) // + .bit(1, EssChannelId.STATE_112)), // + m(new BitsWordElement(4810, this) // + .bit(0, EssChannelId.STATE_113) // + .bit(1, EssChannelId.STATE_114) // + .bit(2, EssChannelId.STATE_115) // + .bit(3, EssChannelId.STATE_116) // + .bit(4, EssChannelId.STATE_117) // + .bit(5, EssChannelId.STATE_118) // + .bit(6, EssChannelId.STATE_119) // + .bit(7, EssChannelId.STATE_120) // + .bit(8, EssChannelId.STATE_121) // + .bit(9, EssChannelId.STATE_122) // + .bit(10, EssChannelId.STATE_123) // + .bit(11, EssChannelId.STATE_124) // + .bit(12, EssChannelId.STATE_125) // + .bit(13, EssChannelId.STATE_126) // + .bit(14, EssChannelId.STATE_127) // + .bit(15, EssChannelId.STATE_128)), // + m(new BitsWordElement(4811, this) // + .bit(0, EssChannelId.STATE_129) // + .bit(1, EssChannelId.STATE_130) // + .bit(2, EssChannelId.STATE_131) // + .bit(3, EssChannelId.STATE_132) // + .bit(4, EssChannelId.STATE_133) // + .bit(5, EssChannelId.STATE_134) // + .bit(6, EssChannelId.STATE_135) // + .bit(7, EssChannelId.STATE_136) // + .bit(8, EssChannelId.STATE_137) // + .bit(9, EssChannelId.STATE_138) // + .bit(10, EssChannelId.STATE_139) // + .bit(11, EssChannelId.STATE_140) // + .bit(12, EssChannelId.STATE_141) // + .bit(13, EssChannelId.STATE_142) // + .bit(14, EssChannelId.STATE_143)), + m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(4812), new ElementToChannelConverter( + // element -> channel + value -> { + // Set SoC to 100 % if battery is full and AllowedCharge is zero + if (value == null) { + return null; + } + int soc = (Integer) value; + IntegerReadChannel allowedCharge = this + .channel(EssChannelId.BECU1_CHARGE_CURRENT_LIMIT); + IntegerReadChannel allowedDischarge = this + .channel(EssChannelId.BECU1_DISCHARGE_CURRENT_LIMIT); + System.out.println(allowedCharge.value().toString() + ", " + + allowedDischarge.value().toString()); + if (soc > 95 && allowedCharge.value().orElse(-1) == 0 + && allowedDischarge.value().orElse(0) != 0) { + return 100; + } else { + return value; + } + }, // + // channel -> element + value -> value)) // ), // new FC3ReadRegistersTask(30166, Priority.LOW, // @@ -346,12 +368,12 @@ public String debugLog() { } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - AsymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(FeneconMiniEss.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + AsymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(FeneconMiniEss.class, accessMode, 300) // .build()); } diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/PcsMode.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/PcsMode.java index 93bf81270db..702c65b8189 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/PcsMode.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/PcsMode.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.mini.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PcsMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetWorkState.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetWorkState.java index f5f23aa92b7..96e13d7cc1d 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetWorkState.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetWorkState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.mini.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SetWorkState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetupMode.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetupMode.java index f035558c0d2..0b9099ae1d3 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetupMode.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SetupMode.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.mini.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SetupMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SystemState.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SystemState.java index ec1466d35ae..7fb75df494a 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SystemState.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/SystemState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.mini.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SystemState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/Config.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/Config.java index 48da51e47ab..c354b61e6f4 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/Config.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/Config.java @@ -7,8 +7,14 @@ name = "FENECON Mini Grid-Meter", // description = "The grid-meter implementation of a FENECON Mini.") @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeter.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeter.java index ff3dda8d660..4a1eeae014f 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeter.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeter.java @@ -70,8 +70,8 @@ public FeneconMiniGridMeter() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), FeneconMiniConstants.UNIT_ID, this.cm, "Modbus", - config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconMiniConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); } @Deactivate diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/Config.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/Config.java index e26f40f06a0..ab812be684a 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/Config.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/Config.java @@ -7,8 +7,14 @@ name = "FENECON Mini PV-Meter", // description = "The pv-meter implementation of a FENECON Mini.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter1"; + @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeter.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeter.java index 24b7fcbb6b2..bc4072751ca 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeter.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeter.java @@ -72,8 +72,8 @@ public FeneconMiniPvMeter() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), FeneconMiniConstants.UNIT_ID, this.cm, "Modbus", - config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), FeneconMiniConstants.UNIT_ID, this.cm, + "Modbus", config.modbus_id()); this.modbusBridgeId = config.modbus_id(); } diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/BatteryGroupState.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/BatteryGroupState.java index 8cb22383317..49444f9f7d2 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/BatteryGroupState.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/BatteryGroupState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BatteryGroupState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/Config.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/Config.java index 8e171e149c8..65ca395009e 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/Config.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/Config.java @@ -7,8 +7,14 @@ name = "FENECON Pro 9-12 Ess", // description = "Implements the FENECON Pro energy storage system.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Modbus-ID", description = "ID of Modbus brige.") diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ControlMode.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ControlMode.java index a3a3def3fe1..00228197fb7 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ControlMode.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ControlMode.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum ControlMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEss.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEss.java index 382ed9683ab..d74a864735c 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEss.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEss.java @@ -17,11 +17,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.channel.AccessMode; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; import io.openems.edge.bridge.modbus.api.element.SignedWordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; @@ -29,6 +31,7 @@ import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.common.channel.EnumWriteChannel; +import io.openems.edge.common.channel.IntegerReadChannel; import io.openems.edge.common.channel.IntegerWriteChannel; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; @@ -80,7 +83,7 @@ public FeneconProEss() { @Override public void applyPower(int activePowerL1, int reactivePowerL1, int activePowerL2, int reactivePowerL2, - int activePowerL3, int reactivePowerL3) throws OpenemsException { + int activePowerL3, int reactivePowerL3) throws OpenemsNamedException { this.getSetActivePowerL1Channel().setNextWriteValue(activePowerL1); this.getSetActivePowerL2Channel().setNextWriteValue(activePowerL2); this.getSetActivePowerL3Channel().setNextWriteValue(activePowerL3); @@ -96,7 +99,8 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), UNIT_ID, this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), UNIT_ID, this.cm, "Modbus", + config.modbus_id()); this.modbusBridgeId = config.modbus_id(); } @@ -117,24 +121,43 @@ protected ModbusProtocol defineModbusProtocol() { m(ProChannelId.CONTROL_MODE, new UnsignedWordElement(101)), // m(ProChannelId.WORK_MODE, new UnsignedWordElement(102)), // new DummyRegisterElement(103), // - m(ProChannelId.TOTAL_BATTERY_CHARGE_ENERGY, new UnsignedDoublewordElement(104)), // - m(ProChannelId.TOTAL_BATTERY_DISCHARGE_ENERGY, new UnsignedDoublewordElement(106)), // + m(SymmetricEss.ChannelId.ACTIVE_CHARGE_ENERGY, new UnsignedDoublewordElement(104)), // + m(SymmetricEss.ChannelId.ACTIVE_DISCHARGE_ENERGY, new UnsignedDoublewordElement(106)), // m(ProChannelId.BATTERY_GROUP_STATE, new UnsignedWordElement(108)), // - m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(109)), // + m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(109), new ElementToChannelConverter( + // element -> channel + value -> { + // Set SoC to 100 % if battery is full and AllowedCharge is zero + if (value == null) { + return null; + } + int soc = (Integer) value; + IntegerReadChannel allowedCharge = this + .channel(ManagedSymmetricEss.ChannelId.ALLOWED_CHARGE_POWER); + IntegerReadChannel allowedDischarge = this + .channel(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER); + if (soc > 95 && allowedCharge.value().orElse(-1) == 0 + && allowedDischarge.value().orElse(0) != 0) { + return 100; + } else { + return value; + } + }, // + // channel -> element + value -> value)), // m(ProChannelId.BATTERY_VOLTAGE, new UnsignedWordElement(110), ElementToChannelConverter.SCALE_FACTOR_2), // m(ProChannelId.BATTERY_CURRENT, new SignedWordElement(111), ElementToChannelConverter.SCALE_FACTOR_2), // m(ProChannelId.BATTERY_POWER, new SignedWordElement(112)), // - bm(new UnsignedWordElement(113))// - .m(ProChannelId.STATE_0, 0) // - .m(ProChannelId.STATE_1, 1) // - .m(ProChannelId.STATE_2, 2) // - .m(ProChannelId.STATE_3, 3) // - .m(ProChannelId.STATE_4, 4) // - .m(ProChannelId.STATE_5, 5) // - .m(ProChannelId.STATE_6, 6) // - .build(), // + m(new BitsWordElement(113, this) // + .bit(0, ProChannelId.STATE_0) // + .bit(1, ProChannelId.STATE_1) // + .bit(2, ProChannelId.STATE_2) // + .bit(3, ProChannelId.STATE_3) // + .bit(4, ProChannelId.STATE_4) // + .bit(5, ProChannelId.STATE_5) // + .bit(6, ProChannelId.STATE_6)), // m(ProChannelId.PCS_OPERATION_STATE, new UnsignedWordElement(114)), // new DummyRegisterElement(115, 117), // m(ProChannelId.CURRENT_L1, new SignedWordElement(118), @@ -169,210 +192,192 @@ protected ModbusProtocol defineModbusProtocol() { m(ManagedSymmetricEss.ChannelId.ALLOWED_DISCHARGE_POWER, new UnsignedWordElement(142)), // new DummyRegisterElement(143, 149)), // new FC3ReadRegistersTask(150, Priority.LOW, // - bm(new UnsignedWordElement(150))// - .m(ProChannelId.STATE_7, 0)// - .m(ProChannelId.STATE_8, 1)// - .m(ProChannelId.STATE_9, 2)// - .m(ProChannelId.STATE_10, 3)// - .m(ProChannelId.STATE_11, 4)// - .m(ProChannelId.STATE_12, 5)// - .m(ProChannelId.STATE_13, 6)// - .m(ProChannelId.STATE_14, 7)// - .m(ProChannelId.STATE_15, 8)// - .m(ProChannelId.STATE_16, 9)// - .m(ProChannelId.STATE_17, 10)// - .build(), // - bm(new UnsignedWordElement(151))// - .m(ProChannelId.STATE_18, 0)// - .build(), // - - bm(new UnsignedWordElement(152))// - .m(ProChannelId.STATE_19, 0)// - .m(ProChannelId.STATE_20, 1)// - .m(ProChannelId.STATE_21, 2)// - .m(ProChannelId.STATE_22, 3)// - .m(ProChannelId.STATE_23, 4)// - .m(ProChannelId.STATE_24, 5)// - .m(ProChannelId.STATE_25, 6)// - .m(ProChannelId.STATE_26, 7)// - .m(ProChannelId.STATE_27, 8)// - .m(ProChannelId.STATE_28, 9)// - .m(ProChannelId.STATE_29, 10)// - .m(ProChannelId.STATE_30, 11)// - .m(ProChannelId.STATE_31, 12)// - .m(ProChannelId.STATE_32, 13)// - .m(ProChannelId.STATE_33, 14)// - .m(ProChannelId.STATE_34, 15)// - .build(), // - - bm(new UnsignedWordElement(153))// - .m(ProChannelId.STATE_35, 0)// - .m(ProChannelId.STATE_36, 1)// - .m(ProChannelId.STATE_37, 2)// - .m(ProChannelId.STATE_38, 3)// - .m(ProChannelId.STATE_39, 4)// - .m(ProChannelId.STATE_40, 5)// - .m(ProChannelId.STATE_41, 6)// - .m(ProChannelId.STATE_42, 7)// - .m(ProChannelId.STATE_43, 8)// - .m(ProChannelId.STATE_44, 9)// - .m(ProChannelId.STATE_45, 10)// - .m(ProChannelId.STATE_46, 11)// - .m(ProChannelId.STATE_47, 12)// - .m(ProChannelId.STATE_48, 13)// - .m(ProChannelId.STATE_49, 14)// - .m(ProChannelId.STATE_50, 15)// - .build(), // - bm(new UnsignedWordElement(154))// - .m(ProChannelId.STATE_51, 0)// - .m(ProChannelId.STATE_52, 1)// - .m(ProChannelId.STATE_53, 2)// - .m(ProChannelId.STATE_54, 3)// - .m(ProChannelId.STATE_55, 4)// - .m(ProChannelId.STATE_56, 5)// - .m(ProChannelId.STATE_57, 6)// - .m(ProChannelId.STATE_58, 7)// - .m(ProChannelId.STATE_59, 8)// - .m(ProChannelId.STATE_60, 9)// - .m(ProChannelId.STATE_61, 10)// - .m(ProChannelId.STATE_62, 11)// - .m(ProChannelId.STATE_63, 12)// - .build(), // - bm(new UnsignedWordElement(155))// - .m(ProChannelId.STATE_64, 0)// - .m(ProChannelId.STATE_65, 1)// - .m(ProChannelId.STATE_66, 2)// - .m(ProChannelId.STATE_67, 3)// - .m(ProChannelId.STATE_68, 4)// - .m(ProChannelId.STATE_69, 5)// - .m(ProChannelId.STATE_70, 6)// - .m(ProChannelId.STATE_71, 7)// - .m(ProChannelId.STATE_72, 8)// - .m(ProChannelId.STATE_73, 9)// - .m(ProChannelId.STATE_74, 10)// - .build(), // - bm(new UnsignedWordElement(156))// - .m(ProChannelId.STATE_75, 0)// - .build(), // - bm(new UnsignedWordElement(157))// - .m(ProChannelId.STATE_76, 0)// - .m(ProChannelId.STATE_77, 1)// - .m(ProChannelId.STATE_78, 2)// - .m(ProChannelId.STATE_79, 3)// - .m(ProChannelId.STATE_80, 4)// - .m(ProChannelId.STATE_81, 5)// - .m(ProChannelId.STATE_82, 6)// - .m(ProChannelId.STATE_83, 7)// - .m(ProChannelId.STATE_84, 8)// - .m(ProChannelId.STATE_85, 9)// - .m(ProChannelId.STATE_86, 10)// - .m(ProChannelId.STATE_87, 11)// - .m(ProChannelId.STATE_88, 12)// - .m(ProChannelId.STATE_89, 13)// - .m(ProChannelId.STATE_90, 14)// - .m(ProChannelId.STATE_91, 15)// - .build(), // - bm(new UnsignedWordElement(158))// - .m(ProChannelId.STATE_92, 0)// - .m(ProChannelId.STATE_93, 1)// - .m(ProChannelId.STATE_94, 2)// - .m(ProChannelId.STATE_95, 3)// - .m(ProChannelId.STATE_96, 4)// - .m(ProChannelId.STATE_97, 5)// - .m(ProChannelId.STATE_98, 6)// - .m(ProChannelId.STATE_99, 7)// - .m(ProChannelId.STATE_100, 8)// - .m(ProChannelId.STATE_101, 9)// - .m(ProChannelId.STATE_102, 10)// - .m(ProChannelId.STATE_103, 11)// - .m(ProChannelId.STATE_104, 12)// - .m(ProChannelId.STATE_105, 13)// - .m(ProChannelId.STATE_106, 14)// - .m(ProChannelId.STATE_107, 15)// - .build(), // - bm(new UnsignedWordElement(159))// - .m(ProChannelId.STATE_108, 0)// - .m(ProChannelId.STATE_109, 1)// - .m(ProChannelId.STATE_110, 2)// - .m(ProChannelId.STATE_111, 3)// - .m(ProChannelId.STATE_112, 4)// - .m(ProChannelId.STATE_113, 5)// - .m(ProChannelId.STATE_114, 6)// - .m(ProChannelId.STATE_115, 7)// - .m(ProChannelId.STATE_116, 8)// - .m(ProChannelId.STATE_117, 9)// - .m(ProChannelId.STATE_118, 10)// - .m(ProChannelId.STATE_119, 11)// - .m(ProChannelId.STATE_120, 12)// - .build(), // - bm(new UnsignedWordElement(160))// - .m(ProChannelId.STATE_121, 0)// - .m(ProChannelId.STATE_122, 1)// - .m(ProChannelId.STATE_123, 2)// - .m(ProChannelId.STATE_124, 3)// - .m(ProChannelId.STATE_125, 4)// - .m(ProChannelId.STATE_126, 5)// - .m(ProChannelId.STATE_127, 6)// - .m(ProChannelId.STATE_128, 7)// - .m(ProChannelId.STATE_129, 8)// - .m(ProChannelId.STATE_130, 9)// - .m(ProChannelId.STATE_131, 10)// - .build(), // - bm(new UnsignedWordElement(161))// - .m(ProChannelId.STATE_132, 0)// - .build(), // - bm(new UnsignedWordElement(162))// - .m(ProChannelId.STATE_133, 0)// - .m(ProChannelId.STATE_134, 1)// - .m(ProChannelId.STATE_135, 2)// - .m(ProChannelId.STATE_136, 3)// - .m(ProChannelId.STATE_137, 4)// - .m(ProChannelId.STATE_138, 5)// - .m(ProChannelId.STATE_139, 6)// - .m(ProChannelId.STATE_140, 7)// - .m(ProChannelId.STATE_141, 8)// - .m(ProChannelId.STATE_142, 9)// - .m(ProChannelId.STATE_143, 10)// - .m(ProChannelId.STATE_144, 11)// - .m(ProChannelId.STATE_145, 12)// - .m(ProChannelId.STATE_146, 13)// - .m(ProChannelId.STATE_147, 14)// - .m(ProChannelId.STATE_148, 15)// - .build(), // - bm(new UnsignedWordElement(163))// - .m(ProChannelId.STATE_149, 0)// - .m(ProChannelId.STATE_150, 1)// - .m(ProChannelId.STATE_151, 2)// - .m(ProChannelId.STATE_152, 3)// - .m(ProChannelId.STATE_153, 4)// - .m(ProChannelId.STATE_154, 5)// - .m(ProChannelId.STATE_155, 6)// - .m(ProChannelId.STATE_156, 7)// - .m(ProChannelId.STATE_157, 8)// - .m(ProChannelId.STATE_158, 9)// - .m(ProChannelId.STATE_159, 10)// - .m(ProChannelId.STATE_160, 11)// - .m(ProChannelId.STATE_161, 12)// - .m(ProChannelId.STATE_162, 13)// - .m(ProChannelId.STATE_163, 14)// - .m(ProChannelId.STATE_164, 15)// - .build(), // - bm(new UnsignedWordElement(164))// - .m(ProChannelId.STATE_165, 0)// - .m(ProChannelId.STATE_166, 1)// - .m(ProChannelId.STATE_167, 2)// - .m(ProChannelId.STATE_168, 3)// - .m(ProChannelId.STATE_169, 4)// - .m(ProChannelId.STATE_170, 5)// - .m(ProChannelId.STATE_171, 6)// - .m(ProChannelId.STATE_172, 7)// - .m(ProChannelId.STATE_173, 8)// - .m(ProChannelId.STATE_174, 9)// - .m(ProChannelId.STATE_175, 10)// - .m(ProChannelId.STATE_176, 11)// - .m(ProChannelId.STATE_177, 12)// - .build()// - ), // + m(new BitsWordElement(150, this) // + .bit(0, ProChannelId.STATE_7) // + .bit(1, ProChannelId.STATE_8) // + .bit(2, ProChannelId.STATE_9) // + .bit(3, ProChannelId.STATE_10) // + .bit(4, ProChannelId.STATE_11) // + .bit(5, ProChannelId.STATE_12) // + .bit(6, ProChannelId.STATE_13) // + .bit(7, ProChannelId.STATE_14) // + .bit(8, ProChannelId.STATE_15) // + .bit(9, ProChannelId.STATE_16) // + .bit(10, ProChannelId.STATE_17)), + m(new BitsWordElement(151, this) // + .bit(0, ProChannelId.STATE_18)), + m(new BitsWordElement(152, this) // + .bit(0, ProChannelId.STATE_19) // + .bit(1, ProChannelId.STATE_20) // + .bit(2, ProChannelId.STATE_21) // + .bit(3, ProChannelId.STATE_22) // + .bit(4, ProChannelId.STATE_23) // + .bit(5, ProChannelId.STATE_24) // + .bit(6, ProChannelId.STATE_25) // + .bit(7, ProChannelId.STATE_26) // + .bit(8, ProChannelId.STATE_27) // + .bit(9, ProChannelId.STATE_28) // + .bit(10, ProChannelId.STATE_29) // + .bit(11, ProChannelId.STATE_30) // + .bit(12, ProChannelId.STATE_31) // + .bit(13, ProChannelId.STATE_32) // + .bit(14, ProChannelId.STATE_33) // + .bit(15, ProChannelId.STATE_34)), + m(new BitsWordElement(153, this) // + .bit(0, ProChannelId.STATE_35) // + .bit(1, ProChannelId.STATE_36) // + .bit(2, ProChannelId.STATE_37) // + .bit(3, ProChannelId.STATE_38) // + .bit(4, ProChannelId.STATE_39) // + .bit(5, ProChannelId.STATE_40) // + .bit(6, ProChannelId.STATE_41) // + .bit(7, ProChannelId.STATE_42) // + .bit(8, ProChannelId.STATE_43) // + .bit(9, ProChannelId.STATE_44) // + .bit(10, ProChannelId.STATE_45) // + .bit(11, ProChannelId.STATE_46) // + .bit(12, ProChannelId.STATE_47) // + .bit(13, ProChannelId.STATE_48) // + .bit(14, ProChannelId.STATE_49) // + .bit(15, ProChannelId.STATE_50)), + m(new BitsWordElement(154, this) // + .bit(0, ProChannelId.STATE_51) // + .bit(1, ProChannelId.STATE_52) // + .bit(2, ProChannelId.STATE_53) // + .bit(3, ProChannelId.STATE_54) // + .bit(4, ProChannelId.STATE_55) // + .bit(5, ProChannelId.STATE_56) // + .bit(6, ProChannelId.STATE_57) // + .bit(7, ProChannelId.STATE_58) // + .bit(8, ProChannelId.STATE_59) // + .bit(9, ProChannelId.STATE_60) // + .bit(10, ProChannelId.STATE_61) // + .bit(11, ProChannelId.STATE_62) // + .bit(12, ProChannelId.STATE_63)), + m(new BitsWordElement(155, this) // + .bit(0, ProChannelId.STATE_64) // + .bit(1, ProChannelId.STATE_65) // + .bit(2, ProChannelId.STATE_66) // + .bit(3, ProChannelId.STATE_67) // + .bit(4, ProChannelId.STATE_68) // + .bit(5, ProChannelId.STATE_69) // + .bit(6, ProChannelId.STATE_70) // + .bit(7, ProChannelId.STATE_71) // + .bit(8, ProChannelId.STATE_72) // + .bit(9, ProChannelId.STATE_73) // + .bit(10, ProChannelId.STATE_74)), + m(new BitsWordElement(156, this) // + .bit(0, ProChannelId.STATE_75)), + m(new BitsWordElement(157, this) // + .bit(0, ProChannelId.STATE_76) // + .bit(1, ProChannelId.STATE_77) // + .bit(2, ProChannelId.STATE_78) // + .bit(3, ProChannelId.STATE_79) // + .bit(4, ProChannelId.STATE_80) // + .bit(5, ProChannelId.STATE_81) // + .bit(6, ProChannelId.STATE_82) // + .bit(7, ProChannelId.STATE_83) // + .bit(8, ProChannelId.STATE_84) // + .bit(9, ProChannelId.STATE_85) // + .bit(10, ProChannelId.STATE_86) // + .bit(11, ProChannelId.STATE_87) // + .bit(12, ProChannelId.STATE_88) // + .bit(13, ProChannelId.STATE_89) // + .bit(14, ProChannelId.STATE_90) // + .bit(15, ProChannelId.STATE_91)), + m(new BitsWordElement(158, this) // + .bit(0, ProChannelId.STATE_92) // + .bit(1, ProChannelId.STATE_93) // + .bit(2, ProChannelId.STATE_94) // + .bit(3, ProChannelId.STATE_95) // + .bit(4, ProChannelId.STATE_96) // + .bit(5, ProChannelId.STATE_97) // + .bit(6, ProChannelId.STATE_98) // + .bit(7, ProChannelId.STATE_99) // + .bit(8, ProChannelId.STATE_100) // + .bit(9, ProChannelId.STATE_101) // + .bit(10, ProChannelId.STATE_102) // + .bit(11, ProChannelId.STATE_103) // + .bit(12, ProChannelId.STATE_104) // + .bit(13, ProChannelId.STATE_105) // + .bit(14, ProChannelId.STATE_106) // + .bit(15, ProChannelId.STATE_107)), + m(new BitsWordElement(159, this) // + .bit(0, ProChannelId.STATE_108) // + .bit(1, ProChannelId.STATE_109) // + .bit(2, ProChannelId.STATE_110) // + .bit(3, ProChannelId.STATE_111) // + .bit(4, ProChannelId.STATE_112) // + .bit(5, ProChannelId.STATE_113) // + .bit(6, ProChannelId.STATE_114) // + .bit(7, ProChannelId.STATE_115) // + .bit(8, ProChannelId.STATE_116) // + .bit(9, ProChannelId.STATE_117) // + .bit(10, ProChannelId.STATE_118) // + .bit(11, ProChannelId.STATE_119) // + .bit(12, ProChannelId.STATE_120)), + m(new BitsWordElement(160, this) // + .bit(0, ProChannelId.STATE_121) // + .bit(1, ProChannelId.STATE_122) // + .bit(2, ProChannelId.STATE_123) // + .bit(3, ProChannelId.STATE_124) // + .bit(4, ProChannelId.STATE_125) // + .bit(5, ProChannelId.STATE_126) // + .bit(6, ProChannelId.STATE_127) // + .bit(7, ProChannelId.STATE_128) // + .bit(8, ProChannelId.STATE_129) // + .bit(9, ProChannelId.STATE_130) // + .bit(10, ProChannelId.STATE_131)), + m(new BitsWordElement(161, this) // + .bit(0, ProChannelId.STATE_132)), + m(new BitsWordElement(162, this) // + .bit(0, ProChannelId.STATE_133) // + .bit(1, ProChannelId.STATE_134) // + .bit(2, ProChannelId.STATE_135) // + .bit(3, ProChannelId.STATE_136) // + .bit(4, ProChannelId.STATE_137) // + .bit(5, ProChannelId.STATE_138) // + .bit(6, ProChannelId.STATE_139) // + .bit(7, ProChannelId.STATE_140) // + .bit(8, ProChannelId.STATE_141) // + .bit(9, ProChannelId.STATE_142) // + .bit(10, ProChannelId.STATE_143) // + .bit(11, ProChannelId.STATE_144) // + .bit(12, ProChannelId.STATE_145) // + .bit(13, ProChannelId.STATE_146) // + .bit(14, ProChannelId.STATE_147) // + .bit(15, ProChannelId.STATE_148)), + m(new BitsWordElement(163, this) // + .bit(0, ProChannelId.STATE_149) // + .bit(1, ProChannelId.STATE_150) // + .bit(2, ProChannelId.STATE_151) // + .bit(3, ProChannelId.STATE_152) // + .bit(4, ProChannelId.STATE_153) // + .bit(5, ProChannelId.STATE_154) // + .bit(6, ProChannelId.STATE_155) // + .bit(7, ProChannelId.STATE_156) // + .bit(8, ProChannelId.STATE_157) // + .bit(9, ProChannelId.STATE_158) // + .bit(10, ProChannelId.STATE_159) // + .bit(11, ProChannelId.STATE_160) // + .bit(12, ProChannelId.STATE_161) // + .bit(13, ProChannelId.STATE_162) // + .bit(14, ProChannelId.STATE_163) // + .bit(15, ProChannelId.STATE_164)), + m(new BitsWordElement(164, this) // + .bit(0, ProChannelId.STATE_165) // + .bit(1, ProChannelId.STATE_166) // + .bit(2, ProChannelId.STATE_167) // + .bit(3, ProChannelId.STATE_168) // + .bit(4, ProChannelId.STATE_169) // + .bit(5, ProChannelId.STATE_170) // + .bit(6, ProChannelId.STATE_171) // + .bit(7, ProChannelId.STATE_172) // + .bit(8, ProChannelId.STATE_173) // + .bit(9, ProChannelId.STATE_174) // + .bit(10, ProChannelId.STATE_175) // + .bit(11, ProChannelId.STATE_176) // + .bit(12, ProChannelId.STATE_177))), // new FC16WriteRegistersTask(200, // m(ProChannelId.SET_WORK_STATE, new UnsignedWordElement(200))), // new FC16WriteRegistersTask(206, // @@ -528,20 +533,20 @@ private void activateRemoteMode() { this.getSetupModeChannel().setNextWriteValue(SetupMode.OFF); } } - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.logError(log, "Unable to activate Remote-Mode: " + e.getMessage()); } } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - AsymmetricEss.getModbusSlaveNatureTable(), // - ManagedSymmetricEss.getModbusSlaveNatureTable(), // - ManagedAsymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(FeneconProEss.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + AsymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedAsymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(FeneconProEss.class, accessMode, 300) // .build()); } diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsMode.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsMode.java index 0c0541e8809..162a9c79622 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsMode.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsMode.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PcsMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsOperationState.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsOperationState.java index 70a1475afa8..e1f115116e6 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsOperationState.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/PcsOperationState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PcsOperationState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ProChannelId.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ProChannelId.java index a23e44ab3dd..fd25fdf50f0 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ProChannelId.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/ProChannelId.java @@ -1,12 +1,12 @@ package io.openems.edge.fenecon.pro.ess; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.EnumReadChannel; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.sum.GridMode; import io.openems.edge.ess.api.SymmetricEss; diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetWorkState.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetWorkState.java index b842569021b..75ee5ca607e 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetWorkState.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetWorkState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SetWorkState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetupMode.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetupMode.java index d66fc8f8d0f..91b3d96f1a8 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetupMode.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SetupMode.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SetupMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SystemState.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SystemState.java index d4d74a58b51..59be9e7e489 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SystemState.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/SystemState.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum SystemState implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/WorkMode.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/WorkMode.java index 7d56876408e..12620995fd3 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/WorkMode.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/WorkMode.java @@ -1,6 +1,6 @@ package io.openems.edge.fenecon.pro.ess; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum WorkMode implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/Config.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/Config.java index 4420d142e80..70806842b91 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/Config.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/Config.java @@ -7,8 +7,14 @@ name = "FENECON Pro 9-12 PV Meter", // description = "Implements the FENECON Pro energy storage system.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter1"; + @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 = "Modbus-ID", description = "ID of Modbus brige.") diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeter.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeter.java index e526bfc1cd5..0b2b4d61067 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeter.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeter.java @@ -1,5 +1,7 @@ package io.openems.edge.fenecon.pro.pvmeter; +import java.util.function.Consumer; + import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; @@ -13,6 +15,7 @@ import org.osgi.service.event.EventConstants; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; @@ -23,11 +26,13 @@ import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; +import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; +import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.common.type.TypeUtils; import io.openems.edge.ess.power.api.Power; import io.openems.edge.meter.api.AsymmetricMeter; import io.openems.edge.meter.api.MeterType; @@ -67,6 +72,17 @@ public FeneconProPvMeter() { ChannelId.values() // ); AsymmetricMeter.initializePowerSumChannels(this); + + // Active Energy + final Consumer> activeEnergySum = ignore -> { + this.getActiveProductionEnergy().setNextValue(TypeUtils.sum(// + this.getActiveProductionEnergyL1().value().get(), // + this.getActiveProductionEnergyL2().value().get(), // + this.getActiveProductionEnergyL3().value().get())); + }; + this.getActiveProductionEnergyL1().onSetNextValue(activeEnergySum); + this.getActiveProductionEnergyL2().onSetNextValue(activeEnergySum); + this.getActiveProductionEnergyL3().onSetNextValue(activeEnergySum); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) @@ -76,7 +92,8 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), UNIT_ID, this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), UNIT_ID, this.cm, "Modbus", + config.modbus_id()); this.modbusBridgeId = config.modbus_id(); } @@ -92,7 +109,7 @@ public String getModbusBridgeId() { @Override protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // - new FC3ReadRegistersTask(121, Priority.HIGH, // + new FC3ReadRegistersTask(121, Priority.LOW, // m(AsymmetricMeter.ChannelId.VOLTAGE_L1, new UnsignedWordElement(121), ElementToChannelConverter.SCALE_FACTOR_2), // m(AsymmetricMeter.ChannelId.VOLTAGE_L2, new UnsignedWordElement(122), @@ -100,19 +117,21 @@ protected ModbusProtocol defineModbusProtocol() { m(AsymmetricMeter.ChannelId.VOLTAGE_L3, new UnsignedWordElement(123), ElementToChannelConverter.SCALE_FACTOR_2)), // - new FC3ReadRegistersTask(2035, Priority.HIGH, // // + new FC3ReadRegistersTask(2035, Priority.LOW, // // m(FeneconProPvMeter.ChannelId.ACTIVE_ENERGY_L1, new UnsignedDoublewordElement(2035), ElementToChannelConverter.SCALE_FACTOR_2), // new DummyRegisterElement(2037, 2065), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new UnsignedWordElement(2066), MINUS_10000_CONVERTER)), // - new FC3ReadRegistersTask(2135, Priority.HIGH, // // - m(FeneconProPvMeter.ChannelId.ACTIVE_ENERGY_L2, new UnsignedDoublewordElement(2135)), // + new FC3ReadRegistersTask(2135, Priority.LOW, // // + m(FeneconProPvMeter.ChannelId.ACTIVE_ENERGY_L2, new UnsignedDoublewordElement(2135), + ElementToChannelConverter.SCALE_FACTOR_2), // new DummyRegisterElement(2137, 2165), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new UnsignedWordElement(2166), MINUS_10000_CONVERTER)), // - new FC3ReadRegistersTask(2235, Priority.HIGH, // // - m(FeneconProPvMeter.ChannelId.ACTIVE_ENERGY_L3, new UnsignedDoublewordElement(2235)), // + new FC3ReadRegistersTask(2235, Priority.LOW, // // + m(FeneconProPvMeter.ChannelId.ACTIVE_ENERGY_L3, new UnsignedDoublewordElement(2235), + ElementToChannelConverter.SCALE_FACTOR_2), // new DummyRegisterElement(2237, 2265), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L3, new UnsignedWordElement(2266), MINUS_10000_CONVERTER))// @@ -149,4 +168,16 @@ public MeterType getMeterType() { public String debugLog() { return "L:" + this.getActivePower().value().asString(); } + + public Channel getActiveProductionEnergyL1() { + return this.channel(ChannelId.ACTIVE_ENERGY_L1); + } + + public Channel getActiveProductionEnergyL2() { + return this.channel(ChannelId.ACTIVE_ENERGY_L2); + } + + public Channel getActiveProductionEnergyL3() { + return this.channel(ChannelId.ACTIVE_ENERGY_L3); + } } diff --git a/io.openems.edge.io.api/src/io/openems/edge/io/test/DummyInputOutput.java b/io.openems.edge.io.api/src/io/openems/edge/io/test/DummyInputOutput.java index c52c3bb2c33..42777188d16 100644 --- a/io.openems.edge.io.api/src/io/openems/edge/io/test/DummyInputOutput.java +++ b/io.openems.edge.io.api/src/io/openems/edge/io/test/DummyInputOutput.java @@ -1,5 +1,6 @@ package io.openems.edge.io.test; +import io.openems.common.channel.AccessMode; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.BooleanReadChannel; import io.openems.edge.common.channel.BooleanWriteChannel; @@ -19,16 +20,16 @@ public class DummyInputOutput extends AbstractOpenemsComponent implements Digita private final BooleanWriteChannel[] ioChannels; public enum ChannelId implements io.openems.edge.common.channel.ChannelId { - INPUT_OUTPUT_0(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_1(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_2(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_3(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_4(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_5(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_6(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_7(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_8(Doc.of(OpenemsType.BOOLEAN)), // - INPUT_OUTPUT_9(Doc.of(OpenemsType.BOOLEAN)); + INPUT_OUTPUT_0(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_1(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_2(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_3(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_4(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_5(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_6(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_7(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_8(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)), // + INPUT_OUTPUT_9(Doc.of(OpenemsType.BOOLEAN).accessMode(AccessMode.READ_WRITE)); private final Doc doc; @@ -63,7 +64,7 @@ public DummyInputOutput(String id) { this.channel(ChannelId.INPUT_OUTPUT_8), // this.channel(ChannelId.INPUT_OUTPUT_9) // }; - super.activate(null, id, true); + super.activate(null, id, "", true); } @Override diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/Config.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/Config.java index b916d761b96..0dd5fbaf056 100644 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/Config.java +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/Config.java @@ -7,8 +7,14 @@ name = "IO KMtronic Relay Board", // description = "Implements the KMtronic Relay Board.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "io0"; + @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 = "Modbus-ID", description = "ID of Modbus brige.") diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java index 8856ced1c57..580f3ffc66f 100644 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java @@ -60,7 +60,7 @@ public KmtronicRelayOutput() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/ThisChannelId.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/ThisChannelId.java index 97d5920031f..5ffac5d2eb2 100644 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/ThisChannelId.java +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/ThisChannelId.java @@ -1,7 +1,7 @@ package io.openems.edge.io.kmtronic; +import io.openems.common.channel.AccessMode; import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.BooleanWriteChannel; import io.openems.edge.common.channel.Doc; diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/Config.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/Config.java index 5ec0c4d2933..1a2e92c80e0 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/Config.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/Config.java @@ -7,8 +7,14 @@ name = "IO WAGO Fieldbus Coupler 750-352", // description = "Implements the WAGO Fieldbus Coupler 750-352") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "io0"; + @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 = "Modbus-ID", description = "ID of Modbus bridge.") diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus501DO2Ch.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus501DO2Ch.java index 5f37c28913e..e97834de95a 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus501DO2Ch.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus501DO2Ch.java @@ -2,12 +2,12 @@ import java.util.concurrent.atomic.AtomicInteger; +import io.openems.common.channel.AccessMode; import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.BooleanReadChannel; import io.openems.edge.common.channel.BooleanWriteChannel; import io.openems.edge.common.channel.internal.OpenemsTypeDoc; -import io.openems.edge.common.channel.AccessMode; public class Fieldbus501DO2Ch extends FieldbusModule { diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java index ff95a7de101..c8d7284edb5 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java @@ -2,9 +2,9 @@ import java.util.concurrent.atomic.AtomicInteger; +import io.openems.common.channel.AccessMode; import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; import io.openems.edge.bridge.modbus.api.element.DummyCoilElement; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.BooleanReadChannel; import io.openems.edge.common.channel.BooleanWriteChannel; diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/Wago.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/Wago.java index c5cb009fd8d..d5775bbf5b4 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/Wago.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/Wago.java @@ -103,7 +103,8 @@ protected void setModbus(BridgeModbusTcp modbus) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), UNIT_ID, this.cm, "Modbus", config.modbus_id()); + super.activate(context, config.id(), config.alias(), config.enabled(), UNIT_ID, this.cm, "Modbus", + config.modbus_id()); /* * Async Create Channels dynamically from ea-config.xml file */ diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/Config.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/Config.java index f85877f206c..16b5f6064f2 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/Config.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/Config.java @@ -1,16 +1,23 @@ package io.openems.edge.kostal.piko.charger; +import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition( // name = "KOSTAL PIKO PV-Charger", // description = "The PV charger implementation of a KOSTAL PIKO.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "charger0"; - String core_id() default "kostalPiko0"; + @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; + String core_id() default "kostalPiko0"; + String webconsole_configurationFactory_nameHint() default "KOSTAL PIKO PV-Charger [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/KostalPikoCharger.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/KostalPikoCharger.java index 7be4b03fdb4..7ffa1e7dac4 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/KostalPikoCharger.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/charger/KostalPikoCharger.java @@ -71,7 +71,7 @@ public KostalPikoCharger() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'Core' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "Core", config.core_id())) { return; diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/BatteryCurrentDirection.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/BatteryCurrentDirection.java index 6cde0321aef..d18238c61b4 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/BatteryCurrentDirection.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/BatteryCurrentDirection.java @@ -1,6 +1,6 @@ package io.openems.edge.kostal.piko.core.api; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum BatteryCurrentDirection implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/KostalPikoCore.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/KostalPikoCore.java index 4cea60c94c2..46f97f12aca 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/KostalPikoCore.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/api/KostalPikoCore.java @@ -1,9 +1,9 @@ package io.openems.edge.kostal.piko.core.api; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Level; -import io.openems.edge.common.channel.Unit; import io.openems.edge.kostal.piko.charger.KostalPikoCharger; import io.openems.edge.kostal.piko.ess.KostalPikoEss; import io.openems.edge.kostal.piko.gridmeter.KostalPikoGridMeter; diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/Config.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/Config.java index 45f0f1bc87a..c309fb2592d 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/Config.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/Config.java @@ -7,8 +7,16 @@ name = "KOSTAL PIKO Core", // description = "Implements a KOSTAL PIKO.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "kostalPiko0"; + @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 = "IP-Address", description = "The IP address") String ip(); @@ -17,8 +25,6 @@ @AttributeDefinition(name = "Port", description = "The Port") int port() default 81; - - boolean enabled() default true; String webconsole_configurationFactory_nameHint() default "KOSTAL PIKO CORE[{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/KostalPikoCoreImpl.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/KostalPikoCoreImpl.java index 2185fdb1cc0..0908297c4da 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/KostalPikoCoreImpl.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/core/impl/KostalPikoCoreImpl.java @@ -295,7 +295,7 @@ public KostalPikoCoreImpl() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); this.socketConnection = new SocketConnection(config.ip(), config.port(), (byte) config.unitID()); Protocol protocol = new Protocol(this, socketConnection); this.worker = new Worker(protocol, this.readTasksManager); diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/Config.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/Config.java index f316ed43c3c..db2d8fb4ffe 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/Config.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/Config.java @@ -1,16 +1,23 @@ package io.openems.edge.kostal.piko.ess; +import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition( // name = "KOSTAL PIKO ESS", // description = "The energy storage system implementation of a KOSTAL PIKO.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; - String core_id() default "kostalPiko0"; + @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; + String core_id() default "kostalPiko0"; + String webconsole_configurationFactory_nameHint() default "KOSTAL PIKO ESS [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/KostalPikoEss.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/KostalPikoEss.java index 38817199af9..914b536c23c 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/KostalPikoEss.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/ess/KostalPikoEss.java @@ -71,7 +71,7 @@ public KostalPikoEss() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'Core' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "Core", config.core_id())) { return; diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/Config.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/Config.java index 2aba45c5786..64523423e08 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/Config.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/Config.java @@ -1,16 +1,23 @@ package io.openems.edge.kostal.piko.gridmeter; +import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition( // name = "KOSTAL PIKO Grid-Meter", // description = "The grid-meter implementation of a KOSTAL PIKO.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter0"; - String core_id() default "kostalPiko0"; + @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; + String core_id() default "kostalPiko0"; + String webconsole_configurationFactory_nameHint() default "KOSTAL PIKO Grid-Meter [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/KostalPikoGridMeter.java b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/KostalPikoGridMeter.java index b7d84517a5f..2e33b3a666a 100644 --- a/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/KostalPikoGridMeter.java +++ b/io.openems.edge.kostal.piko/src/io/openems/edge/kostal/piko/gridmeter/KostalPikoGridMeter.java @@ -72,7 +72,7 @@ public KostalPikoGridMeter() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'Core' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "Core", config.core_id())) { return; diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java index f87cef41075..aabf9c35d74 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java @@ -2,10 +2,10 @@ import java.util.function.Consumer; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.type.TypeUtils; diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhase.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhase.java new file mode 100644 index 00000000000..4ed0100f2d2 --- /dev/null +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhase.java @@ -0,0 +1,18 @@ +package io.openems.edge.meter.api; + +public enum SinglePhase { + L1("L1"), // + L2("L2"), // + L3("L3"); + + private final String symbol; + + private SinglePhase(String symbol) { + this.symbol = symbol; + } + + public String getSymbol() { + return symbol; + } + +} diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhaseMeter.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhaseMeter.java new file mode 100644 index 00000000000..337d37c305f --- /dev/null +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhaseMeter.java @@ -0,0 +1,58 @@ +package io.openems.edge.meter.api; + +import org.osgi.annotation.versioning.ProviderType; + +import io.openems.edge.common.channel.Doc; + +@ProviderType +public interface SinglePhaseMeter extends AsymmetricMeter { + + 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; + } + } + + /** + * Gets the Phase this ESS is connected to. + * + * @return the Phase + */ + public SinglePhase getPhase(); + + /** + * Initializes Channel listeners. Copies the Active-Power Phase-Channel value to + * Active-Power Channel. + * + * @param meter the AsymmetricMeter + * @param phase the Phase + */ + public static void initializeCopyPhaseChannel(AsymmetricMeter ess, SinglePhase phase) { + switch (phase) { + case L1: + ess.getActivePowerL1().onSetNextValue(value -> { + ess.getActivePower().setNextValue(value); + }); + break; + case L2: + ess.getActivePowerL2().onSetNextValue(value -> { + ess.getActivePower().setNextValue(value); + }); + break; + case L3: + ess.getActivePowerL3().onSetNextValue(value -> { + ess.getActivePower().setNextValue(value); + }); + break; + } + } +} diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java index 9a42867c2a9..0448d17f02e 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java @@ -1,13 +1,13 @@ package io.openems.edge.meter.api; import io.openems.common.OpenemsConstants; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.common.utils.IntUtils; import io.openems.common.utils.IntUtils.Round; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.IntegerDoc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; /** @@ -263,4 +263,13 @@ default Channel getMinActivePower() { default Channel getMaxActivePower() { return this.channel(ChannelId.MAX_ACTIVE_POWER); } + + /** + * Gets the Current in [mA]. + * + * @return the Channel + */ + default Channel getCurrent(){ + return this.channel(ChannelId.CURRENT); + } } diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/test/DummySymmetricMeter.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/test/DummySymmetricMeter.java index 37a87fee795..f388c0d4908 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/meter/test/DummySymmetricMeter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/test/DummySymmetricMeter.java @@ -22,7 +22,7 @@ public DummySymmetricMeter(String id) { for (Channel channel : this.channels()) { channel.nextProcessImage(); } - super.activate(null, id, true); + super.activate(null, id, "", true); } @Override diff --git a/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/api/SymmetricPvInverter.java b/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/api/SymmetricPvInverter.java index eded46ede33..baab8d87540 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/api/SymmetricPvInverter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/api/SymmetricPvInverter.java @@ -1,10 +1,10 @@ package io.openems.edge.pvinverter.api; -import io.openems.edge.common.channel.AccessMode; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.IntegerDoc; import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.meter.api.MeterType; import io.openems.edge.meter.api.SymmetricMeter; diff --git a/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/test/DummySymmetricPvInverter.java b/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/test/DummySymmetricPvInverter.java index 6ca53b648f1..80898c25c61 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/test/DummySymmetricPvInverter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/pvinverter/test/DummySymmetricPvInverter.java @@ -21,7 +21,7 @@ public DummySymmetricPvInverter(String id) { for (Channel channel : this.channels()) { channel.nextProcessImage(); } - super.activate(null, id, true); + super.activate(null, id, "", true); } } diff --git a/io.openems.edge.meter.artemes.am2/.classpath b/io.openems.edge.meter.artemes.am2/.classpath new file mode 100644 index 00000000000..3ebd512b99a --- /dev/null +++ b/io.openems.edge.meter.artemes.am2/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/io.openems.edge.meter.artemes.am2/.gitignore b/io.openems.edge.meter.artemes.am2/.gitignore new file mode 100644 index 00000000000..c2b941a96de --- /dev/null +++ b/io.openems.edge.meter.artemes.am2/.gitignore @@ -0,0 +1,2 @@ +/bin_test/ +/generated/ diff --git a/io.openems.edge.meter.artemes.am2/.project b/io.openems.edge.meter.artemes.am2/.project new file mode 100644 index 00000000000..6d1417eecdc --- /dev/null +++ b/io.openems.edge.meter.artemes.am2/.project @@ -0,0 +1,23 @@ + + + io.openems.edge.meter.artemes.am2 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.edge.meter.artemes.am2/bnd.bnd b/io.openems.edge.meter.artemes.am2/bnd.bnd new file mode 100644 index 00000000000..3653c097061 --- /dev/null +++ b/io.openems.edge.meter.artemes.am2/bnd.bnd @@ -0,0 +1,25 @@ +Bundle-Name: OpenEMS Edge Meter Artemes AM-2 +Bundle-Vendor: FENECON GmbH +Bundle-License: https://opensource.org/licenses/EPL-2.0 +Bundle-Version: 1.0.0.${tstamp} +Bundle-Description: This bundle implments the Artemes AM2 meter. + +Export-Package: \ + io.openems.edge.meter.api,\ + io.openems.edge.meter.asymmetric.api,\ + io.openems.edge.meter.symmetric.api + +Private-Package: \ + io.openems.edge.meter.artemes.am2 + +-includeresource: {readme.md} + +-buildpath: ${buildpath},\ + io.openems.edge.meter.api;version=latest,\ + io.openems.edge.bridge.modbus;version=latest,\ + io.openems.edge.common;version=latest + +-testpath: ${testpath} + +javac.source: 1.8 +javac.target: 1.8 \ No newline at end of file diff --git a/io.openems.edge.meter.artemes.am2/doc/AM-2_User manual.pdf b/io.openems.edge.meter.artemes.am2/doc/AM-2_User manual.pdf new file mode 100644 index 00000000000..af493572f26 Binary files /dev/null and b/io.openems.edge.meter.artemes.am2/doc/AM-2_User manual.pdf differ diff --git a/io.openems.edge.meter.artemes.am2/readme.md b/io.openems.edge.meter.artemes.am2/readme.md new file mode 100644 index 00000000000..7b50b875482 --- /dev/null +++ b/io.openems.edge.meter.artemes.am2/readme.md @@ -0,0 +1,8 @@ +# io.openems.edge.meter.artemes.am2 Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/Config.java b/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/Config.java new file mode 100644 index 00000000000..a2987c53e21 --- /dev/null +++ b/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/Config.java @@ -0,0 +1,34 @@ +package io.openems.edge.meter.artemes.am2; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.edge.meter.api.MeterType; + +@ObjectClassDefinition(name = "Meter Artemes AM-2", description = "Implements the Artemes AM-2 meter.") +@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 = "Grid, Production (=default), Consumption") + MeterType type() default MeterType.PRODUCTION; + + @AttributeDefinition(name = "Modbus-ID", description = "Id of the modbus bridge") + String modbus_id(); + + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device") + int modbusUnitId(); + + @AttributeDefinition(name = "Modbus target filter", description = " This is auto-generated by 'Modbus-ID'") + String Modbus_target() default ""; + + String webconsole_configurationFactory_nameHint() default "Meter Artemes AM-2 [{id}]"; + +} \ No newline at end of file diff --git a/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/MeterArtemesAM2.java b/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/MeterArtemesAM2.java new file mode 100644 index 00000000000..138b91b4b39 --- /dev/null +++ b/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/MeterArtemesAM2.java @@ -0,0 +1,134 @@ +package io.openems.edge.meter.artemes.am2; + +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.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; +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.SignedDoublewordElement; +import io.openems.edge.bridge.modbus.api.element.SignedQuadruplewordElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedQuadruplewordElement; +import io.openems.edge.bridge.modbus.api.task.FC4ReadInputRegistersTask; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.meter.api.AsymmetricMeter; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SymmetricMeter; + +@Designate(ocd = Config.class, factory = true) +@Component(name = "Meter.Artemes.AM2", // + immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE) +public class MeterArtemesAM2 extends AbstractOpenemsModbusComponent + implements SymmetricMeter, AsymmetricMeter, OpenemsComponent { + + private MeterType metertype = MeterType.PRODUCTION; + + @Reference + protected ConfigurationAdmin cm; + + public MeterArtemesAM2() { + super(// + OpenemsComponent.ChannelId.values(), // + AsymmetricMeter.ChannelId.values(), // + SymmetricMeter.ChannelId.values(), // + ChannelId.values() // + ); + } + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + @Activate + void activate(ComponentContext context, Config config) { + this.metertype = config.type(); + + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + config.modbus_id()); + } + + @Deactivate + protected void deativate() { + super.deactivate(); + } + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + ; + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + public Doc doc() { + return this.doc; + } + } + + @Override + public MeterType getMeterType() { + return this.metertype; + } + + @Override + protected ModbusProtocol defineModbusProtocol() { + return new ModbusProtocol(this, + new FC4ReadInputRegistersTask(0x0000, Priority.HIGH, + m(AsymmetricMeter.ChannelId.VOLTAGE_L1, new UnsignedDoublewordElement(0x0000)), + m(AsymmetricMeter.ChannelId.VOLTAGE_L2, new UnsignedDoublewordElement(0x0002)), + m(AsymmetricMeter.ChannelId.VOLTAGE_L3, new UnsignedDoublewordElement(0x0004)), + new DummyRegisterElement(0x0006, 0x000B), + m(SymmetricMeter.ChannelId.VOLTAGE, new UnsignedDoublewordElement(0x000C)), + m(AsymmetricMeter.ChannelId.CURRENT_L1, new SignedDoublewordElement(0x000E)), + m(AsymmetricMeter.ChannelId.CURRENT_L2, new SignedDoublewordElement(0x0010)), + m(AsymmetricMeter.ChannelId.CURRENT_L3, new SignedDoublewordElement(0x0012)), + new DummyRegisterElement(0x0014, 0x0015), + m(SymmetricMeter.ChannelId.CURRENT, new SignedDoublewordElement(0x0016)), + m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new SignedQuadruplewordElement(0x0018), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new SignedQuadruplewordElement(0x001C), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L3, new SignedQuadruplewordElement(0X0020), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + m(SymmetricMeter.ChannelId.ACTIVE_POWER, new SignedQuadruplewordElement(0X0024), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + new DummyRegisterElement(0x0028, 0x0037), + m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L1, new SignedQuadruplewordElement(0x0038), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L2, new SignedQuadruplewordElement(0x003C), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L3, new SignedQuadruplewordElement(0x0040), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + m(SymmetricMeter.ChannelId.REACTIVE_POWER, new SignedQuadruplewordElement(0x0044), + ElementToChannelConverter.SCALE_FACTOR_MINUS_3), + new DummyRegisterElement(0x0048, 0x0071), + m(SymmetricMeter.ChannelId.FREQUENCY, new UnsignedDoublewordElement(0x0072))), + + new FC4ReadInputRegistersTask(0x0418, Priority.LOW, + m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedQuadruplewordElement(0x0418), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), + m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedQuadruplewordElement(0x0041C), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1))); + } + + @Override + public String debugLog() { + return "L:" + this.getActivePower().value().asString(); + } + +} diff --git a/io.openems.edge.meter.artemes.am2/test/.gitignore b/io.openems.edge.meter.artemes.am2/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/Config.java b/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/Config.java index ac89bbaf497..c002df71092 100644 --- a/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/Config.java +++ b/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/Config.java @@ -9,8 +9,14 @@ name = "Meter B-Control EM300", // description = "Implements the B-Control EM300 meter.") @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?") diff --git a/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300.java b/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300.java index 00bf2cb1399..dd26bbaa9ae 100644 --- a/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300.java +++ b/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300.java @@ -15,6 +15,7 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; @@ -26,7 +27,6 @@ import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.common.taskmanager.Priority; @@ -66,7 +66,7 @@ protected void setModbus(BridgeModbus modbus) { void activate(ComponentContext context, Config config) { this.meterType = config.type(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } @@ -164,12 +164,12 @@ protected ModbusProtocol defineModbusProtocol() { new DummyRegisterElement(48, 55), // new DummyRegisterElement(56, 59), // Apparent Power L1 - cm(new UnsignedDoublewordElement(60)) // + m(new UnsignedDoublewordElement(60)) // .m(AsymmetricMeter.ChannelId.CURRENT_L1, ElementToChannelConverter.DIRECT_1_TO_1) // .m(SymmetricMeter.ChannelId.CURRENT, ElementToChannelConverter.DIRECT_1_TO_1) // .build(), // - cm(new UnsignedDoublewordElement(62)) // + m(new UnsignedDoublewordElement(62)) // .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.DIRECT_1_TO_1) // .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.DIRECT_1_TO_1) // .build(), // diff --git a/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/Config.java b/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/Config.java index d386344efd8..afd6e15f085 100644 --- a/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/Config.java +++ b/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/Config.java @@ -9,8 +9,14 @@ name = "Meter Carlo Gavazzi EM300", // description = "Implements the Carlo Gavazzi EM300-series meter.") @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?") diff --git a/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300.java b/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300.java index 96414060568..ddfb18b234c 100644 --- a/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300.java +++ b/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300.java @@ -12,6 +12,7 @@ import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; @@ -22,7 +23,6 @@ import io.openems.edge.bridge.modbus.api.element.WordOrder; import io.openems.edge.bridge.modbus.api.task.FC4ReadInputRegistersTask; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.taskmanager.Priority; import io.openems.edge.meter.api.AsymmetricMeter; @@ -57,7 +57,7 @@ protected void setModbus(BridgeModbus modbus) { void activate(ComponentContext context, Config config) { this.meterType = config.type(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } diff --git a/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/Config.java b/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/Config.java index 9faed457317..0ef44efa5ea 100644 --- a/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/Config.java +++ b/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/Config.java @@ -9,8 +9,14 @@ name = "Meter Janitza UMG 96RM-E", // description = "Implements the Janitza UMG 96RM-E 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?") @@ -22,7 +28,7 @@ @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, i.e. Power is multiplied with -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'.") diff --git a/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rme.java b/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rme.java index 4d824998505..f8f518c5a81 100644 --- a/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rme.java +++ b/io.openems.edge.meter.janitza.umg96rme/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rme.java @@ -65,7 +65,7 @@ void activate(ComponentContext context, Config config) { this.meterType = config.type(); this.invert = config.invert(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } @@ -103,7 +103,7 @@ protected ModbusProtocol defineModbusProtocol() { m(SymmetricMeter.ChannelId.FREQUENCY, new FloatDoublewordElement(800), ElementToChannelConverter.SCALE_FACTOR_3), new DummyRegisterElement(802, 807), // - cm(new FloatDoublewordElement(808)) // + m(new FloatDoublewordElement(808)) // .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.SCALE_FACTOR_3) // .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.SCALE_FACTOR_3) // .build(), // @@ -113,13 +113,13 @@ protected ModbusProtocol defineModbusProtocol() { ElementToChannelConverter.SCALE_FACTOR_3), new DummyRegisterElement(814, 859), // m(AsymmetricMeter.ChannelId.CURRENT_L1, new FloatDoublewordElement(860), - ElementToChannelConverter.SCALE_FACTOR_3), + ElementToChannelConverter.SCALE_FACTOR_3_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.CURRENT_L2, new FloatDoublewordElement(862), - ElementToChannelConverter.SCALE_FACTOR_3), + ElementToChannelConverter.SCALE_FACTOR_3_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.CURRENT_L3, new FloatDoublewordElement(864), - ElementToChannelConverter.SCALE_FACTOR_3), + ElementToChannelConverter.SCALE_FACTOR_3_AND_INVERT_IF_TRUE(this.invert)), m(SymmetricMeter.ChannelId.CURRENT, new FloatDoublewordElement(866), - ElementToChannelConverter.SCALE_FACTOR_3), + ElementToChannelConverter.SCALE_FACTOR_3_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new FloatDoublewordElement(868), ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new FloatDoublewordElement(870), diff --git a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/Config.java b/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/Config.java index c7327d33407..bc143d871c1 100644 --- a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/Config.java +++ b/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/Config.java @@ -4,12 +4,17 @@ import org.osgi.service.metatype.annotations.ObjectClassDefinition; import io.openems.edge.meter.api.MeterType; -@ObjectClassDefinition( - name = "Meter Microcare SDM 630", // +@ObjectClassDefinition(name = "Meter Microcare SDM 630", // description = "Implements the Microcare SDM630 meter.") @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 = "Grid (default), Production, Consumption") diff --git a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSDM630.java b/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSDM630.java index af25e07378a..09dc4d209ca 100644 --- a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSDM630.java +++ b/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSDM630.java @@ -14,6 +14,7 @@ import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; @@ -24,7 +25,6 @@ import io.openems.edge.bridge.modbus.api.element.WordOrder; import io.openems.edge.bridge.modbus.api.task.FC4ReadInputRegistersTask; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.taskmanager.Priority; import io.openems.edge.meter.api.AsymmetricMeter; @@ -59,7 +59,7 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { this.meterType = config.type(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } diff --git a/io.openems.edge.meter.socomec/bnd.bnd b/io.openems.edge.meter.socomec/bnd.bnd index 426a97f516f..76b3b7468f7 100644 --- a/io.openems.edge.meter.socomec/bnd.bnd +++ b/io.openems.edge.meter.socomec/bnd.bnd @@ -7,6 +7,7 @@ Export-Package: \ io.openems.edge.meter.asymmetric.api,\ io.openems.edge.meter.symmetric.api Private-Package: \ + io.openems.edge.meter.socomec.countise14,\ io.openems.edge.meter.socomec.dirisa14,\ io.openems.edge.meter.socomec.dirise24,\ io.openems.edge.meter.socomec.dirisa10,\ diff --git a/io.openems.edge.meter.socomec/doc/MODBUS tables of Countis_E14.html b/io.openems.edge.meter.socomec/doc/MODBUS tables of Countis_E14.html new file mode 100644 index 00000000000..ed4021bbced --- /dev/null +++ b/io.openems.edge.meter.socomec/doc/MODBUS tables of Countis_E14.html @@ -0,0 +1,4104 @@ + + + + + + MODBUS tables of Countis_E14 + + + + +
    +
    +
    logosocomec.png
    +
    +

    General


    Identification


    + + + + + + + + + + + + + + + + + + + +
    +

    Product identification


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    500000xC350Info66NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    500000xC3504"SOCO"-STRING_16
    500040xC3541Product order ID (Countis:100, Protection:200, Atys:300, Diris:400)-U16
    500050xC3551Product ID (EX: 1000 ATS3)-U16
    500060xC3561JBUS Table Version (EX: 101 Version 1.01)-U16
    500070xC3571Product software version (EX: 100 Version 1.00)-U16
    500080xC3581Serial_AA_SS-U16_HEX
    500090xC3591Serial_SST_L-U16_HEX
    500100xC35A1Serial_order-U16
    500110xC35B2Serial_Reserve-U32
    500130xC35D4See "Code table" tab for more details-U64_HEX
    500170xC3611Reserved - -
    500180xC3621Product version (Major)-U16
    500190xC3631Product version (Minor)-U16
    500200xC3641Reserved - -
    500210xC3651Reserved - -
    500220xC3663Reserved - -
    500250xC3691Reserved - -
    500260xC36A1Reserved - -
    500270xC36B1Reserved - -
    500280xC36C1Reserved - -
    500290xC36D1Reserved - -
    500300xC36E4Product VLO (EX : "880100")-STRING_NORM
    500340xC3724Reserved - -
    500380xC3764Reserved - -
    500420xC37A8Vendor name (EX : "SOCOMEC")-STRING_NORM
    500500xC3828Product name (EX : "DIRIS A40R")-STRING_NORM
    500580xC38A8Extended name-STRING_NORM


    Common settings


    + + + + + + + + + + + + + + + + + + + +
    +

    Modbus settings


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    573440xE000Settings1USERREADREAD | WRITE | WRITE_MANY

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    573440xE0001Slave Address-U8

    + + + + + + + + + + + + + + + + + + + +
    +

    RS485 settings


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    573450xE001Settings3USERREADREAD | WRITE | WRITE_MANY

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    573450xE0011Baudrate :
    0 : 1200
    1 : 2400
    2 : 4800
    3 : 9600
    4 : 19200
    5 : 38400
    6 : 57600
    7 : 115200
    -U16
    573460xE0021Stop bit :
    0 : 1
    1 : 2
    -U16
    573470xE0031Parity :
    0 : None
    1 : Odd
    2 : Even
    -U16


    Actions


    + + + + + + + + + + + + + + + + + + + +
    +

    Action system


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    578560xE200Commands1NONEWRITEWRITE

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    578560xE2001Action :
    0xA1 : Product Configuration storage
    0xB2 : Product reset
    -U8_HEX

    + + + + + + + + + + + + + + + + + + + +
    +

    Value reset


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    578880xE220Commands5NONEWRITEWRITE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    578880xE2201Reserved - -
    578890xE2211Reserved - -
    578900xE2221Reset :
    0x0001 : Partial Ea+
    0x0002 : Partial Er+
    0x0004 : Partial Eap
    0x0008 : Partial Ea-
    0x0010 : Partial Er-
    -U16_HEX
    578910xE2231Reserved - -
    578920xE2241Reset :
    0x0001 : Tariff counter 1
    0x0002 : Tariff counter 2
    0xFFFF : All
    -U16_HEX



    Application


    Measurement


    Common


    + + + + + + + + + + + + + + + + + + + +
    +

    Metrology Affected by current and voltage transformers


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    505120xC550Info62NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    505120xC5502Reserved - -
    505140xC5522Reserved - -
    505160xC5542Reserved - -
    505180xC5562Reserved - -
    505200xC5582Simple voltage : V1V / 100U32
    505220xC55A2Reserved - -
    505240xC55C2Reserved - -
    505260xC55E2Frequency : FHz / 1000U32
    505280xC5602Current : I1A / 1000U32
    505300xC5622Reserved - -
    505320xC5642Reserved - -
    505340xC5662Reserved - -
    505360xC5682Sum Active Power +/- : PW / 0.1S32
    505380xC56A2Sum Reactive Power +/- : Qvar / 0.1S32
    505400xC56C2Sum Apparent Power : SVA / 0.1U32
    505420xC56E2Sum Power Factor : -: leading et + : lagging : PF- / 1000S32
    505440xC5702Active Power phase 1 +/- : P1W / 0.1S32
    505460xC5722Reserved - -
    505480xC5742Reserved - -
    505500xC5762Reactive Power phase 1 +/- : Q1var / 0.1S32
    505520xC5782Reserved - -
    505540xC57A2Reserved - -
    505560xC57C2Apparent Power phase 1 : S1VA / 0.1U32
    505580xC57E2Reserved - -
    505600xC5802Reserved - -
    505620xC5822Power Factor phase 1 -: leading and + : lagging : PF1- / 1000S32
    505640xC5842Reserved - -
    505660xC5862Reserved - -
    505680xC5882Reserved - -
    505700xC58A2Reserved - -
    505720xC58C2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Metrology No Affected by current and voltage transformers


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    512800xC850Info36NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    512800xC8501Reserved - -
    512810xC8511Reserved - -
    512820xC8521Reserved - -
    512830xC8531Reserved - -
    512840xC8541Simple voltage : V1V / 100U16
    512850xC8551Reserved - -
    512860xC8561Reserved - -
    512870xC8571Frequency : FHz / 1000U16
    512880xC8581Reserved - -
    512890xC8591Reserved - -
    512900xC85A1Reserved - -
    512910xC85B1Reserved - -
    512920xC85C1Reserved - -
    512930xC85D1Reserved - -
    512940xC85E1Reserved - -
    512950xC85F1Sum power factor : -: leading and + : lagging : PF- / 1000S16
    512960xC8601Reserved - -
    512970xC8611Reserved - -
    512980xC8621Reserved - -
    512990xC8631Reserved - -
    513000xC8641Reserved - -
    513010xC8651Reserved - -
    513020xC8661Reserved - -
    513030xC8671Reserved - -
    513040xC8681Reserved - -
    513050xC8691Power Factor phase 1 -: leading and + : lagging : PF1- / 1000S16
    513060xC86A1Reserved - -
    513070xC86B1Reserved - -
    513080xC86C1Reserved - -
    513090xC86D1Reserved - -
    513100xC86E1Reserved - -
    513110xC86F1Total Positive Active Energy (no resetable) : Ea+Wh / 1E-06U16
    513120xC8701Total Positive Reactive Energy (no resetable) : Er +varh / 1E-06U16
    513130xC8711Total Negative Active Energy (no resetable) : Ea-Wh / 1E-06U16
    513140xC8721Total Negative Reactive Energy (no resetable) : Er -varh / 1E-06U16
    513150xC8731Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    507680xC650Info65NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    507680xC6502Reserved - -
    507700xC6522Total Positive Active Energy (no resetable) : Ea+Wh / 0.001U32
    507720xC6542Total Positive Reactive Energy (no resetable) : Er +varh / 0.001U32
    507740xC6562Total Apparent Energy (no resetable) : EapVAh / 0.001U32
    507760xC6582Total Negative Active Energy (no resetable) : Ea-Wh / 0.001U32
    507780xC65A2Total Negative Reactive Energy (no resetable) : Er -varh / 0.001U32
    507800xC65C2Partial Positive Active Energy: Ea+Wh / 0.001U32
    507820xC65E2Partial Positive Reactive Energy: Er +varh / 0.001U32
    507840xC6602Partial Apparent Energy : EapVAh / 0.001U32
    507860xC6622Partial Negative Active Energy : Ea-Wh / 0.001U32
    507880xC6642Partial Negative Reactive Energy : Er -varh / 0.001U32
    507900xC6662Reserved - -
    507920xC6682Reserved - -
    507940xC66A2Reserved - -
    507960xC66C2Reserved - -
    507980xC66E2Reserved - -
    508000xC6702Reserved - -
    508020xC6722Reserved - -
    508040xC6742Reserved - -
    508060xC6762Reserved - -
    508080xC6782Reserved - -
    508100xC67A2Reserved - -
    508120xC67C2Reserved - -
    508140xC67E2Reserved - -
    508160xC6802Reserved - -
    508180xC6822Reserved - -
    508200xC6842Reserved - -
    508220xC6862Reserved - -
    508240xC6882Reserved - -
    508260xC68A2Reserved - -
    508280xC68C1Reserved - -
    508290xC68D1Reserved - -
    508300xC68E1Reserved - -
    508310xC68F1Reserved - -
    508320xC6901Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies in Unit/100


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    509440xC700Info65NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    509440xC7002Reserved - -
    509460xC7022Total Positive Active Energy (no resetable) : Ea+Wh / 0.1U32
    509480xC7042Total Positive Reactive Energy (no resetable) : Er +varh / 0.1U32
    509500xC7062Reserved - -
    509520xC7082Total Negative Active Energy (no resetable) : Ea-Wh / 0.1U32
    509540xC70A2Total Negative Reactive Energy (no resetable) : Er -varh / 0.1U32
    509560xC70C2Partial Positive Active Energy: Ea+Wh / 0.1U32
    509580xC70E2Partial Positive Reactive Energy: Er +varh / 0.1U32
    509600xC7102Reserved - -
    509620xC7122Partial Negative Active Energy : Ea-Wh / 0.1U32
    509640xC7142Partial Negative Reactive Energy : Er -varh / 0.1U32
    509660xC7162Reserved - -
    509680xC7182Reserved - -
    509700xC71A2Reserved - -
    509720xC71C2Reserved - -
    509740xC71E2Reserved - -
    509760xC7202Reserved - -
    509780xC7222Reserved - -
    509800xC7242Reserved - -
    509820xC7262Reserved - -
    509840xC7282Reserved - -
    509860xC72A2Reserved - -
    509880xC72C2Reserved - -
    509900xC72E2Reserved - -
    509920xC7302Reserved - -
    509940xC7322Reserved - -
    509960xC7342Reserved - -
    509980xC7362Reserved - -
    510000xC7382Reserved - -
    510020xC73A2Reserved - -
    510040xC73C1Reserved - -
    510050xC73D1Reserved - -
    510060xC73E1Reserved - -
    510070xC73F1Reserved - -
    510080xC7401Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies per tariff


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    508480xC6A0Info50NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    508480xC6A011 >= Tariff number <= 2-U8
    508490xC6A11Tariff number in progress ( 1 to 2 )-U8
    508500xC6A22Positive Active Energies 1Wh / 0.001U32
    508520xC6A42Positive Active Energies 2Wh / 0.001U32
    508540xC6A62Reserved - -
    508560xC6A82Reserved - -
    508580xC6AA2Reserved - -
    508600xC6AC2Reserved - -
    508620xC6AE2Reserved - -
    508640xC6B02Reserved - -
    508660xC6B22Positive Reactive Enegies 1varh / 0.001U32
    508680xC6B42Positive Reactive Enegies 2varh / 0.001U32
    508700xC6B62Reserved - -
    508720xC6B82Reserved - -
    508740xC6BA2Reserved - -
    508760xC6BC2Reserved - -
    508780xC6BE2Reserved - -
    508800xC6C02Reserved - -
    508820xC6C22Time Counter 1h / 100U32
    508840xC6C42Time Counter 2h / 100U32
    508860xC6C62Reserved - -
    508880xC6C82Reserved - -
    508900xC6CA2Reserved - -
    508920xC6CC2Reserved - -
    508940xC6CE2Reserved - -
    508960xC6D02Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies per tariff in Unit/100


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    512000xC800Info50NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    512000xC80011 >= Tariff number <= 2-U8
    512010xC8011Tariff number in progress ( 1 to 2 )-U8
    512020xC8022Positive Active Energies 1Wh / 0.1U32
    512040xC8042Positive Active Energies 2Wh / 0.1U32
    512060xC8062Reserved - -
    512080xC8082Reserved - -
    512100xC80A2Reserved - -
    512120xC80C2Reserved - -
    512140xC80E2Reserved - -
    512160xC8102Reserved - -
    512180xC8122Positive Reactive Enegies 1varh / 0.1U32
    512200xC8142Positive Reactive Enegies 2varh / 0.1U32
    512220xC8162Reserved - -
    512240xC8182Reserved - -
    512260xC81A2Reserved - -
    512280xC81C2Reserved - -
    512300xC81E2Reserved - -
    512320xC8202Reserved - -
    512340xC8222Time Counter 1h / 100U32
    512360xC8242Time Counter 2h / 100U32
    512380xC8262Reserved - -
    512400xC8282Reserved - -
    512420xC82A2Reserved - -
    512440xC82C2Reserved - -
    512460xC82E2Reserved - -
    512480xC8302Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energy measurement - Total


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    259840x6580Info66NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    259840x65801Reserved - -
    259850x65812Reserved - -
    259870x65832Total Positive active Energy : Ea+Wh / 0.001U32
    259890x65851Total Residual positive active Energy : rEa+Wh / 10U16
    259900x65862Total Negative active Energy : Ea-Wh / 0.001U32
    259920x65881Total Residual negative active Energy : rEa-Wh / 10U16
    259930x65892Total Positive reactive Energy : Er+varh / 0.001U32
    259950x658B1Total Residual positive reactive Energy : rEr+varh / 10U16
    259960x658C2Total Negative reactive Energy : Er-varh / 0.001U32
    259980x658E1Total Residual negative reactive Energy : rEr-varh / 10U16
    259990x658F2Total Apparent Energy : EapVAh / 0.001U32
    260010x65911Total Residual apparent Energy : rEapVAh / 10U16
    260020x65922Total positive lagging reactive Energy : Er+ (lagging)varh / 0.001U32
    260040x65941Total residual positive lagging reactive Energy : rEr+ (lagging)varh / 10U16
    260050x65952Total negative lagging reactive Energy : Er- (lagging)varh / 0.001U32
    260070x65971Total residual negative lagging reactive Energy : rEr- (lagging)varh / 10U16
    260080x65982Total positive leading reactive Energy : Er+ (leading)varh / 0.001U32
    260100x659A1Total residual positive leading reactive Energy : rEr+ (leading)varh / 10U16
    260110x659B2Total negative leading reactive Energy : Er- (leading)varh / 0.001U32
    260130x659D1Total residual negative leading reactive Energy : rEr- (leading)varh / 10U16
    260140x659E2Reserved - -
    260160x65A02Reserved - -
    260180x65A21Reserved - -
    260190x65A32Reserved - -
    260210x65A51Reserved - -
    260220x65A62Reserved - -
    260240x65A81Reserved - -
    260250x65A92Reserved - -
    260270x65AB1Reserved - -
    260280x65AC2Reserved - -
    260300x65AE1Reserved - -
    260310x65AF2Reserved - -
    260330x65B12Reserved - -
    260350x65B32Reserved - -
    260370x65B51Reserved - -
    260380x65B62Reserved - -
    260400x65B81Reserved - -
    260410x65B92Reserved - -
    260430x65BB1Reserved - -
    260440x65BC2Reserved - -
    260460x65BE1Reserved - -
    260470x65BF2Reserved - -
    260490x65C11Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Partial active energy by tariff


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    327680x8000Info48NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    327680x80002Partial positive active Energy : Ea+ Tariff 1Wh / 0.001U32
    327700x80021Partial Residual positive active Energy : rEa+ Tariff 1Wh / 10U16
    327710x80032Partial positive active Energy : Ea+ Tariff 2Wh / 0.001U32
    327730x80051Partial Residual positive active Energy : rEa+ Tariff 2Wh / 10U16
    327740x80062Reserved - -
    327760x80081Reserved - -
    327770x80092Reserved - -
    327790x800B1Reserved - -
    327800x800C2Reserved - -
    327820x800E1Reserved - -
    327830x800F2Reserved - -
    327850x80111Reserved - -
    327860x80122Reserved - -
    327880x80141Reserved - -
    327890x80152Reserved - -
    327910x80171Reserved - -
    327920x80182Partial positive active Energy : Ea- Tariff 1Wh / 0.001U32
    327940x801A1Partial Residual positive active Energy : rEa- Tariff 1Wh / 10U16
    327950x801B2Partial positive active Energy : Ea- Tariff 2Wh / 0.001U32
    327970x801D1Partial Residual positive active Energy : rEa- Tariff 2Wh / 10U16
    327980x801E2Reserved - -
    328000x80201Reserved - -
    328010x80212Reserved - -
    328030x80231Reserved - -
    328040x80242Reserved - -
    328060x80261Reserved - -
    328070x80272Reserved - -
    328090x80291Reserved - -
    328100x802A2Reserved - -
    328120x802C1Reserved - -
    328130x802D2Reserved - -
    328150x802F1Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energy balance


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    330240x8100Info6NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    330240x81002Net active energy balance (SUM ((Ea+)-(Ea-)) by tariff)Wh / 0.001U32
    330260x81021Net Residual active energy balance (SUM((Ea+)-(Ea-)) by tariff)Wh / 10U16
    330270x81032Net reactive energy balance (SUM((Er+)-(Er-)) by tariff)Wh / 0.001U32
    330290x81051Net Residual reactive energy balance (SUM((Er+)-(Er-)) by tariff)Wh / 10U16

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Total (1st Part)


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    263680x6700Info33NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    263680x67001Tariff number in progress (available for 1st,2nd and 3rd Parts below)
    1 : Tariff 1
    2 : Tariff 2
    -U16
    263690x67012T1 - Ea+Wh / 0.001U32
    263710x67032T2 - Ea+Wh / 0.001U32
    263730x67052Reserved - -
    263750x67072Reserved - -
    263770x67092Reserved - -
    263790x670B2Reserved - -
    263810x670D2Reserved - -
    263830x670F2Reserved - -
    263850x67112T1 - Ea-Wh / 0.001U32
    263870x67132T2 - Ea-Wh / 0.001U32
    263890x67152Reserved - -
    263910x67172Reserved - -
    263930x67192Reserved - -
    263950x671B2Reserved - -
    263970x671D2Reserved - -
    263990x671F2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Total (2nd Part)


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    264010x6721Info96NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    264010x67212T1 - Er+varh / 0.001U32
    264030x67232T2 - Er+varh / 0.001U32
    264050x67252Reserved - -
    264070x67272Reserved - -
    264090x67292Reserved - -
    264110x672B2Reserved - -
    264130x672D2Reserved - -
    264150x672F2Reserved - -
    264170x67312T1 - Er+ (lagging)varh / 0.001U32
    264190x67332T2 - Er+ (lagging)varh / 0.001U32
    264210x67352Reserved - -
    264230x67372Reserved - -
    264250x67392Reserved - -
    264270x673B2Reserved - -
    264290x673D2Reserved - -
    264310x673F2Reserved - -
    264330x67412T1 - Er+ (leading)varh / 0.001U32
    264350x67432T2 - Er+ (leading)varh / 0.001U32
    264370x67452Reserved - -
    264390x67472Reserved - -
    264410x67492Reserved - -
    264430x674B2Reserved - -
    264450x674D2Reserved - -
    264470x674F2Reserved - -
    264490x67512T1 - Er-varh / 0.001U32
    264510x67532T2 - Er-varh / 0.001U32
    264530x67552Reserved - -
    264550x67572Reserved - -
    264570x67592Reserved - -
    264590x675B2Reserved - -
    264610x675D2Reserved - -
    264630x675F2Reserved - -
    264650x67612T1 - Er- (lagging)varh / 0.001U32
    264670x67632T2 - Er- (lagging)varh / 0.001U32
    264690x67652Reserved - -
    264710x67672Reserved - -
    264730x67692Reserved - -
    264750x676B2Reserved - -
    264770x676D2Reserved - -
    264790x676F2Reserved - -
    264810x67712T1 - Er- (leading)varh / 0.001U32
    264830x67732T2 - Er- (leading)varh / 0.001U32
    264850x67752Reserved - -
    264870x67772Reserved - -
    264890x67792Reserved - -
    264910x677B2Reserved - -
    264930x677D2Reserved - -
    264950x677F2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Total (3rd Part)


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    264970x6781Info16NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    264970x67812T1 - EapVAh / 0.001U32
    264990x67832T2 - EapVAh / 0.001U32
    265010x67852Reserved - -
    265030x67872Reserved - -
    265050x67892Reserved - -
    265070x678B2Reserved - -
    265090x678D2Reserved - -
    265110x678F2Reserved - -


    Specific


    + + + + + + + + + + + + + + + + + + + +
    +

    Table Overflow


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    368640x9000Info11NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    368640x90002Value of the meter overflow (10Wh) Wh / 0.1U32
    368660x90021Nb Overflow Total Ea +-U8
    368670x90031Nb Overflow Total Er +-U8
    368680x90041Nb Overflow Total Ea --U8
    368690x90051Nb Overflow Total Er --U8
    368700x90061Nb Overflow Total Eap-U8
    368710x90071Nb Overflow Total Ea T1-U8
    368720x90081Nb Overflow Total Ea T2-U8
    368730x90091Reserved - -
    368740x900A1Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Current without CT


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    405760x9E80Info6NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    405760x9E802Current: I1A / 1000U32
    405780x9E822Reserved - -
    405800x9E842Reserved - -


    Action


    + + + + + + + + + + + + + + + + + + + +
    +

    Set Tariff


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    404800x9E20Commands2NONEWRITEWRITE

    + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    404800x9E201Reserved - -
    404810x9E211Tariff Number to set-U8

    + + + + + + + + + + + + + + + + + + + +
    +

    Partial Energies Reset


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    405120x9E40Commands1NONEWRITEWRITE

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    405120x9E401Reset
    0x20 : Partial Energies
    -U8_HEX




    Industrialization


    + + + + + + + + + + + + + + + + + + + +
    +

    Indus Mode and MID status


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    405440x9E60Info2NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    405440x9E601Indus Mode (0: indus mode disabled, 1: indus mode enabled)-U8
    405450x9E611MID status (0: non-MID product, 1: MID product-U8

    + + + + + + + + + + + + + + + + + + + +
    +

    COM Board Product ID


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    406080x9EA0Info1NONEREADREAD

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    406080x9EA01Product ID :0x01: E3x/E4x COM Board0x02: E63 COM Board-U8


    + + \ No newline at end of file diff --git a/io.openems.edge.meter.socomec/doc/MODBUS tables of Countis_E24.html b/io.openems.edge.meter.socomec/doc/MODBUS tables of Countis_E24.html new file mode 100644 index 00000000000..60a48d9665d --- /dev/null +++ b/io.openems.edge.meter.socomec/doc/MODBUS tables of Countis_E24.html @@ -0,0 +1,7068 @@ + + + + + + MODBUS tables of Countis_E24 + + + + +
    +
    +
    logosocomec.png
    +
    +

    General


    Identification


    + + + + + + + + + + + + + + + + + + + +
    +

    Product identification


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    500000xC350Info66NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    500000xC3504"SOCO"-STRING_16
    500040xC3541Product order ID (Countis:100, Protection:200, Atys:300, Diris:400)-U16
    500050xC3551Product ID (EX: 1000 ATS3)-U16
    500060xC3561JBUS Table Version (EX: 101 Version 1.01)-U16
    500070xC3571Product software version (EX: 100 Version 1.00)-U16
    500080xC3581Serial_AA_SS-U16_HEX
    500090xC3591Serial_SST_L-U16_HEX
    500100xC35A1Serial_order-U16
    500110xC35B2Serial_Reserve-U32
    500130xC35D4See "Code table" tab for more details-U64_HEX
    500170xC3611Reserved - -
    500180xC3621Product version (Major)-U16
    500190xC3631Product version (Minor)-U16
    500200xC3641Reserved - -
    500210xC3651Reserved - -
    500220xC3663Reserved - -
    500250xC3691Reserved - -
    500260xC36A1Reserved - -
    500270xC36B1Reserved - -
    500280xC36C1Reserved - -
    500290xC36D1Reserved - -
    500300xC36E4Product VLO (EX : "880100")-STRING_NORM
    500340xC3724Reserved - -
    500380xC3764Reserved - -
    500420xC37A8Vendor name (EX : "SOCOMEC")-STRING_NORM
    500500xC3828Product name (EX : "DIRIS A40R")-STRING_NORM
    500580xC38A8Extended name-STRING_NORM


    Common settings


    + + + + + + + + + + + + + + + + + + + +
    +

    Modbus settings


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    573440xE000Settings1USERREADREAD | WRITE | WRITE_MANY

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    573440xE0001Slave Address-U8

    + + + + + + + + + + + + + + + + + + + +
    +

    RS485 settings


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    573450xE001Settings3USERREADREAD | WRITE | WRITE_MANY

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    573450xE0011Baudrate :
    0 : 1200
    1 : 2400
    2 : 4800
    3 : 9600
    4 : 19200
    5 : 38400
    6 : 57600
    7 : 115200
    -U16
    573460xE0021Stop bit :
    0 : 1
    1 : 2
    -U16
    573470xE0031Parity :
    0 : None
    1 : Odd
    2 : Even
    -U16


    Actions


    + + + + + + + + + + + + + + + + + + + +
    +

    Action system


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    578560xE200Commands1NONEWRITEWRITE

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    578560xE2001Action :
    0xA1 : Product Configuration storage
    0xB2 : Product reset
    -U8_HEX

    + + + + + + + + + + + + + + + + + + + +
    +

    Value reset


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    578880xE220Commands5NONEWRITEWRITE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    578880xE2201Reserved - -
    578890xE2211Reserved - -
    578900xE2221Reset :
    0x0001 : Partial Ea+
    0x0002 : Partial Er+
    0x0004 : Partial Eap
    0x0008 : Partial Ea-
    0x0010 : Partial Er-
    -U16_HEX
    578910xE2231Reserved - -
    578920xE2241Reset :
    0x0001 : Tariff counter 1
    0x0002 : Tariff counter 2
    0xFFFF : All
    -U16_HEX



    Application


    Measurement


    Common


    + + + + + + + + + + + + + + + + + + + +
    +

    Metrology Affected by current and voltage transformers


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    505120xC550Info62NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    505120xC5502Reserved - -
    505140xC5522Phase to Phase Voltage: U12V / 100U32
    505160xC5542Phase to Phase Voltage: U23V / 100U32
    505180xC5562Phase to Phase Voltage: U31V / 100U32
    505200xC5582Simple voltage : V1V / 100U32
    505220xC55A2Simple voltage : V2V / 100U32
    505240xC55C2Simple voltage : V3V / 100U32
    505260xC55E2Frequency : FHz / 1000U32
    505280xC5602Current : I1A / 1000U32
    505300xC5622Current : I2A / 1000U32
    505320xC5642Current : I3A / 1000U32
    505340xC5662Neutral Current : InA / 1000U32
    505360xC5682Sum Active Power +/- : PW / 0.1S32
    505380xC56A2Sum Reactive Power +/- : Qvar / 0.1S32
    505400xC56C2Sum Apparent Power : SVA / 0.1U32
    505420xC56E2Sum Power Factor : -: leading et + : lagging : PF- / 1000S32
    505440xC5702Active Power phase 1 +/- : P1W / 0.1S32
    505460xC5722Active Power phase 2 +/- : P2W / 0.1S32
    505480xC5742Active Power phase 3 +/- : P3W / 0.1S32
    505500xC5762Reactive Power phase 1 +/- : Q1var / 0.1S32
    505520xC5782Reactive Power phase 2 +/- : Q2var / 0.1S32
    505540xC57A2Reactive Power phase 3 +/- : Q3var / 0.1S32
    505560xC57C2Apparent Power phase 1 : S1VA / 0.1U32
    505580xC57E2Apparent Power phase 2 : S2VA / 0.1U32
    505600xC5802Apparent Power phase 3 : S3VA / 0.1U32
    505620xC5822Power Factor phase 1 -: leading and + : lagging : PF1- / 1000S32
    505640xC5842Power Factor phase 2 -: leading and + : lagging : PF2- / 1000S32
    505660xC5862Power Factor phase 3 -: leading and + : lagging : PF3- / 1000S32
    505680xC5882System value I Sys : ( I1+I2+I3) / 3A / 1000U32
    505700xC58A2System value U Sys : (U12 + U23 + U31 ) / 3V / 100U32
    505720xC58C2System value V Sys : (V1 + V2 + V3 ) / 3V / 100U32

    + + + + + + + + + + + + + + + + + + + +
    +

    Metrology No Affected by current and voltage transformers


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    512800xC850Info36NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    512800xC8501Reserved - -
    512810xC8511Phase to Phase Voltage: U12V / 100U16
    512820xC8521Phase to Phase Voltage: U23V / 100U16
    512830xC8531Phase to Phase Voltage: U31V / 100U16
    512840xC8541Simple voltage : V1V / 100U16
    512850xC8551Simple voltage : V2V / 100U16
    512860xC8561Simple voltage : V3V / 100U16
    512870xC8571Frequency : FHz / 1000U16
    512880xC8581Reserved - -
    512890xC8591Reserved - -
    512900xC85A1Reserved - -
    512910xC85B1Reserved - -
    512920xC85C1Reserved - -
    512930xC85D1Reserved - -
    512940xC85E1Reserved - -
    512950xC85F1Sum power factor : -: leading and + : lagging : PF- / 1000S16
    512960xC8601Reserved - -
    512970xC8611Reserved - -
    512980xC8621Reserved - -
    512990xC8631Reserved - -
    513000xC8641Reserved - -
    513010xC8651Reserved - -
    513020xC8661Reserved - -
    513030xC8671Reserved - -
    513040xC8681Reserved - -
    513050xC8691Power Factor phase 1 -: leading and + : lagging : PF1- / 1000S16
    513060xC86A1Power Factor phase 2 -: leading and + : lagging : PF2- / 1000S16
    513070xC86B1Power Factor phase 3 -: leading and + : lagging : PF3- / 1000S16
    513080xC86C1System value I Sys : ( I1+I2+I3) / 3A / 1000U16
    513090xC86D1System value U Sys : (U12 + U23 + U31 ) / 3V / 100U16
    513100xC86E1System value V Sys : (V1 + V2 + V3 ) / 3V / 100U16
    513110xC86F1Total Positive Active Energy (no resetable) : Ea+Wh / 1E-06U16
    513120xC8701Total Positive Reactive Energy (no resetable) : Er +varh / 1E-06U16
    513130xC8711Total Negative Active Energy (no resetable) : Ea-Wh / 1E-06U16
    513140xC8721Total Negative Reactive Energy (no resetable) : Er -varh / 1E-06U16
    513150xC8731Phase sequence: 0=123 CW, 1=132 CCW, 3=ND-U16

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    507680xC650Info65NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    507680xC6502Reserved - -
    507700xC6522Total Positive Active Energy (no resetable) : Ea+Wh / 0.001U32
    507720xC6542Total Positive Reactive Energy (no resetable) : Er +varh / 0.001U32
    507740xC6562Total Apparent Energy (no resetable) : EapVAh / 0.001U32
    507760xC6582Total Negative Active Energy (no resetable) : Ea-Wh / 0.001U32
    507780xC65A2Total Negative Reactive Energy (no resetable) : Er -varh / 0.001U32
    507800xC65C2Partial Positive Active Energy: Ea+Wh / 0.001U32
    507820xC65E2Partial Positive Reactive Energy: Er +varh / 0.001U32
    507840xC6602Partial Apparent Energy : EapVAh / 0.001U32
    507860xC6622Partial Negative Active Energy : Ea-Wh / 0.001U32
    507880xC6642Partial Negative Reactive Energy : Er -varh / 0.001U32
    507900xC6662Reserved - -
    507920xC6682Reserved - -
    507940xC66A2Reserved - -
    507960xC66C2Reserved - -
    507980xC66E2Reserved - -
    508000xC6702Reserved - -
    508020xC6722Reserved - -
    508040xC6742Reserved - -
    508060xC6762Reserved - -
    508080xC6782Reserved - -
    508100xC67A2Reserved - -
    508120xC67C2Reserved - -
    508140xC67E2Reserved - -
    508160xC6802Reserved - -
    508180xC6822Reserved - -
    508200xC6842Reserved - -
    508220xC6862Reserved - -
    508240xC6882Reserved - -
    508260xC68A2Reserved - -
    508280xC68C1Reserved - -
    508290xC68D1Reserved - -
    508300xC68E1Reserved - -
    508310xC68F1Reserved - -
    508320xC6901Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies in Unit/100


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    509440xC700Info65NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    509440xC7002Reserved - -
    509460xC7022Total Positive Active Energy (no resetable) : Ea+Wh / 0.1U32
    509480xC7042Total Positive Reactive Energy (no resetable) : Er +varh / 0.1U32
    509500xC7062Total Apparent Energy (no resetable) : EapVAh / 0.1U32
    509520xC7082Total Negative Active Energy (no resetable) : Ea-Wh / 0.1U32
    509540xC70A2Total Negative Reactive Energy (no resetable) : Er -varh / 0.1U32
    509560xC70C2Partial Positive Active Energy: Ea+Wh / 0.1U32
    509580xC70E2Partial Positive Reactive Energy: Er +varh / 0.1U32
    509600xC7102Partial Apparent Energy : EapVAh / 0.1U32
    509620xC7122Partial Negative Active Energy : Ea-Wh / 0.1U32
    509640xC7142Partial Negative Reactive Energy : Er -varh / 0.1U32
    509660xC7162Reserved - -
    509680xC7182Reserved - -
    509700xC71A2Reserved - -
    509720xC71C2Reserved - -
    509740xC71E2Reserved - -
    509760xC7202Reserved - -
    509780xC7222Reserved - -
    509800xC7242Reserved - -
    509820xC7262Reserved - -
    509840xC7282Reserved - -
    509860xC72A2Reserved - -
    509880xC72C2Reserved - -
    509900xC72E2Reserved - -
    509920xC7302Reserved - -
    509940xC7322Reserved - -
    509960xC7342Reserved - -
    509980xC7362Reserved - -
    510000xC7382Reserved - -
    510020xC73A2Reserved - -
    510040xC73C1Reserved - -
    510050xC73D1Reserved - -
    510060xC73E1Reserved - -
    510070xC73F1Reserved - -
    510080xC7401Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies per tariff


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    508480xC6A0Info50NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    508480xC6A011 >= Tariff number <= 2-U8
    508490xC6A11Tariff number in progress ( 1 to 2 )-U8
    508500xC6A22Positive Active Energies 1Wh / 0.001U32
    508520xC6A42Positive Active Energies 2Wh / 0.001U32
    508540xC6A62Reserved - -
    508560xC6A82Reserved - -
    508580xC6AA2Reserved - -
    508600xC6AC2Reserved - -
    508620xC6AE2Reserved - -
    508640xC6B02Reserved - -
    508660xC6B22Positive Reactive Enegies 1varh / 0.001U32
    508680xC6B42Positive Reactive Enegies 2varh / 0.001U32
    508700xC6B62Reserved - -
    508720xC6B82Reserved - -
    508740xC6BA2Reserved - -
    508760xC6BC2Reserved - -
    508780xC6BE2Reserved - -
    508800xC6C02Reserved - -
    508820xC6C22Time Counter 1h / 100U32
    508840xC6C42Time Counter 2h / 100U32
    508860xC6C62Reserved - -
    508880xC6C82Reserved - -
    508900xC6CA2Reserved - -
    508920xC6CC2Reserved - -
    508940xC6CE2Reserved - -
    508960xC6D02Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energies per tariff in Unit/100


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    512000xC800Info50NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    512000xC80011 >= Tariff number <= 2-U8
    512010xC8011Tariff number in progress ( 1 to 2 )-U8
    512020xC8022Positive Active Energies 1Wh / 0.1U32
    512040xC8042Positive Active Energies 2Wh / 0.1U32
    512060xC8062Reserved - -
    512080xC8082Reserved - -
    512100xC80A2Reserved - -
    512120xC80C2Reserved - -
    512140xC80E2Reserved - -
    512160xC8102Reserved - -
    512180xC8122Positive Reactive Enegies 1varh / 0.1U32
    512200xC8142Positive Reactive Enegies 2varh / 0.1U32
    512220xC8162Reserved - -
    512240xC8182Reserved - -
    512260xC81A2Reserved - -
    512280xC81C2Reserved - -
    512300xC81E2Reserved - -
    512320xC8202Reserved - -
    512340xC8222Time Counter 1h / 100U32
    512360xC8242Time Counter 2h / 100U32
    512380xC8262Reserved - -
    512400xC8282Reserved - -
    512420xC82A2Reserved - -
    512440xC82C2Reserved - -
    512460xC82E2Reserved - -
    512480xC8302Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energy measurement - Line #1


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    198400x4D80Info66NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    198400x4D801Reserved - -
    198410x4D812Reserved - -
    198430x4D832Total Positive active Energy : Ea+Wh / 0.001U32
    198450x4D851Total Residual positive active Energy : rEa+Wh / 10U16
    198460x4D862Total Negative active Energy : Ea-Wh / 0.001U32
    198480x4D881Total Residual negative active Energy : rEa-Wh / 10U16
    198490x4D892Total Positive reactive Energy : Er+varh / 0.001U32
    198510x4D8B1Total Residual positive reactive Energy : rEr+varh / 10U16
    198520x4D8C2Total Negative reactive Energy : Er-varh / 0.001U32
    198540x4D8E1Total Residual negative reactive Energy : rEr-varh / 10U16
    198550x4D8F2Total Apparent Energy : EapVAh / 0.001U32
    198570x4D911Total Residual apparent Energy : rEapVAh / 10U16
    198580x4D922Total positive lagging reactive Energy : Er+ (lagging)varh / 0.001U32
    198600x4D941Total residual positive lagging reactive Energy : rEr+ (lagging)varh / 10U16
    198610x4D952Total negative lagging reactive Energy : Er- (lagging)varh / 0.001U32
    198630x4D971Total residual negative lagging reactive Energy : rEr- (lagging)varh / 10U16
    198640x4D982Total positive leading reactive Energy : Er+ (leading)varh / 0.001U32
    198660x4D9A1Total residual positive leading reactive Energy : rEr+ (leading)varh / 10U16
    198670x4D9B2Total negative leading reactive Energy : Er- (leading)varh / 0.001U32
    198690x4D9D1Total residual negative leading reactive Energy : rEr- (leading)varh / 10U16
    198700x4D9E2Reserved - -
    198720x4DA02Reserved - -
    198740x4DA21Reserved - -
    198750x4DA32Reserved - -
    198770x4DA51Reserved - -
    198780x4DA62Reserved - -
    198800x4DA81Reserved - -
    198810x4DA92Reserved - -
    198830x4DAB1Reserved - -
    198840x4DAC2Reserved - -
    198860x4DAE1Reserved - -
    198870x4DAF2Reserved - -
    198890x4DB12Reserved - -
    198910x4DB32Reserved - -
    198930x4DB51Reserved - -
    198940x4DB62Reserved - -
    198960x4DB81Reserved - -
    198970x4DB92Reserved - -
    198990x4DBB1Reserved - -
    199000x4DBC2Reserved - -
    199020x4DBE1Reserved - -
    199030x4DBF2Reserved - -
    199050x4DC11Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energy measurement - Line #2


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    218880x5580Info66NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    218880x55801Reserved - -
    218890x55812Reserved - -
    218910x55832Total Positive active Energy : Ea+Wh / 0.001U32
    218930x55851Total Residual positive active Energy : rEa+Wh / 10U16
    218940x55862Total Negative active Energy : Ea-Wh / 0.001U32
    218960x55881Total Residual negative active Energy : rEa-Wh / 10U16
    218970x55892Total Positive reactive Energy : Er+varh / 0.001U32
    218990x558B1Total Residual positive reactive Energy : rEr+varh / 10U16
    219000x558C2Total Negative reactive Energy : Er-varh / 0.001U32
    219020x558E1Total Residual negative reactive Energy : rEr-varh / 10U16
    219030x558F2Total Apparent Energy : EapVAh / 0.001U32
    219050x55911Total Residual apparent Energy : rEapVAh / 10U16
    219060x55922Total positive lagging reactive Energy : Er+ (lagging)varh / 0.001U32
    219080x55941Total residual positive lagging reactive Energy : rEr+ (lagging)varh / 10U16
    219090x55952Total negative lagging reactive Energy : Er- (lagging)varh / 0.001U32
    219110x55971Total residual negative lagging reactive Energy : rEr- (lagging)varh / 10U16
    219120x55982Total positive leading reactive Energy : Er+ (leading)varh / 0.001U32
    219140x559A1Total residual positive leading reactive Energy : rEr+ (leading)varh / 10U16
    219150x559B2Total negative leading reactive Energy : Er- (leading)varh / 0.001U32
    219170x559D1Total residual negative leading reactive Energy : rEr- (leading)varh / 10U16
    219180x559E2Reserved - -
    219200x55A02Reserved - -
    219220x55A21Reserved - -
    219230x55A32Reserved - -
    219250x55A51Reserved - -
    219260x55A62Reserved - -
    219280x55A81Reserved - -
    219290x55A92Reserved - -
    219310x55AB1Reserved - -
    219320x55AC2Reserved - -
    219340x55AE1Reserved - -
    219350x55AF2Reserved - -
    219370x55B12Reserved - -
    219390x55B32Reserved - -
    219410x55B51Reserved - -
    219420x55B62Reserved - -
    219440x55B81Reserved - -
    219450x55B92Reserved - -
    219470x55BB1Reserved - -
    219480x55BC2Reserved - -
    219500x55BE1Reserved - -
    219510x55BF2Reserved - -
    219530x55C11Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energy measurement - Line #3


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    239360x5D80Info66NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    239360x5D801Reserved - -
    239370x5D812Reserved - -
    239390x5D832Total Positive active Energy : Ea+Wh / 0.001U32
    239410x5D851Total Residual positive active Energy : rEa+Wh / 10U16
    239420x5D862Total Negative active Energy : Ea-Wh / 0.001U32
    239440x5D881Total Residual negative active Energy : rEa-Wh / 10U16
    239450x5D892Total Positive reactive Energy : Er+varh / 0.001U32
    239470x5D8B1Total Residual positive reactive Energy : rEr+varh / 10U16
    239480x5D8C2Total Negative reactive Energy : Er-varh / 0.001U32
    239500x5D8E1Total Residual negative reactive Energy : rEr-varh / 10U16
    239510x5D8F2Total Apparent Energy : EapVAh / 0.001U32
    239530x5D911Total Residual apparent Energy : rEapVAh / 10U16
    239540x5D922Total positive lagging reactive Energy : Er+ (lagging)varh / 0.001U32
    239560x5D941Total residual positive lagging reactive Energy : rEr+ (lagging)varh / 10U16
    239570x5D952Total negative lagging reactive Energy : Er- (lagging)varh / 0.001U32
    239590x5D971Total residual negative lagging reactive Energy : rEr- (lagging)varh / 10U16
    239600x5D982Total positive leading reactive Energy : Er+ (leading)varh / 0.001U32
    239620x5D9A1Total residual positive leading reactive Energy : rEr+ (leading)varh / 10U16
    239630x5D9B2Total negative leading reactive Energy : Er- (leading)varh / 0.001U32
    239650x5D9D1Total residual negative leading reactive Energy : rEr- (leading)varh / 10U16
    239660x5D9E2Reserved - -
    239680x5DA02Reserved - -
    239700x5DA21Reserved - -
    239710x5DA32Reserved - -
    239730x5DA51Reserved - -
    239740x5DA62Reserved - -
    239760x5DA81Reserved - -
    239770x5DA92Reserved - -
    239790x5DAB1Reserved - -
    239800x5DAC2Reserved - -
    239820x5DAE1Reserved - -
    239830x5DAF2Reserved - -
    239850x5DB12Reserved - -
    239870x5DB32Reserved - -
    239890x5DB51Reserved - -
    239900x5DB62Reserved - -
    239920x5DB81Reserved - -
    239930x5DB92Reserved - -
    239950x5DBB1Reserved - -
    239960x5DBC2Reserved - -
    239980x5DBE1Reserved - -
    239990x5DBF2Reserved - -
    240010x5DC11Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energy measurement - Total


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    259840x6580Info66NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    259840x65801Reserved - -
    259850x65812Reserved - -
    259870x65832Total Positive active Energy : Ea+Wh / 0.001U32
    259890x65851Total Residual positive active Energy : rEa+Wh / 10U16
    259900x65862Total Negative active Energy : Ea-Wh / 0.001U32
    259920x65881Total Residual negative active Energy : rEa-Wh / 10U16
    259930x65892Total Positive reactive Energy : Er+varh / 0.001U32
    259950x658B1Total Residual positive reactive Energy : rEr+varh / 10U16
    259960x658C2Total Negative reactive Energy : Er-varh / 0.001U32
    259980x658E1Total Residual negative reactive Energy : rEr-varh / 10U16
    259990x658F2Total Apparent Energy : EapVAh / 0.001U32
    260010x65911Total Residual apparent Energy : rEapVAh / 10U16
    260020x65922Total positive lagging reactive Energy : Er+ (lagging)varh / 0.001U32
    260040x65941Total residual positive lagging reactive Energy : rEr+ (lagging)varh / 10U16
    260050x65952Total negative lagging reactive Energy : Er- (lagging)varh / 0.001U32
    260070x65971Total residual negative lagging reactive Energy : rEr- (lagging)varh / 10U16
    260080x65982Total positive leading reactive Energy : Er+ (leading)varh / 0.001U32
    260100x659A1Total residual positive leading reactive Energy : rEr+ (leading)varh / 10U16
    260110x659B2Total negative leading reactive Energy : Er- (leading)varh / 0.001U32
    260130x659D1Total residual negative leading reactive Energy : rEr- (leading)varh / 10U16
    260140x659E2Reserved - -
    260160x65A02Reserved - -
    260180x65A21Reserved - -
    260190x65A32Reserved - -
    260210x65A51Reserved - -
    260220x65A62Reserved - -
    260240x65A81Reserved - -
    260250x65A92Reserved - -
    260270x65AB1Reserved - -
    260280x65AC2Reserved - -
    260300x65AE1Reserved - -
    260310x65AF2Reserved - -
    260330x65B12Reserved - -
    260350x65B32Reserved - -
    260370x65B51Reserved - -
    260380x65B62Reserved - -
    260400x65B81Reserved - -
    260410x65B92Reserved - -
    260430x65BB1Reserved - -
    260440x65BC2Reserved - -
    260460x65BE1Reserved - -
    260470x65BF2Reserved - -
    260490x65C11Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Partial active energy by tariff


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    327680x8000Info48NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    327680x80002Partial positive active Energy : Ea+ Tariff 1Wh / 0.001U32
    327700x80021Partial Residual positive active Energy : rEa+ Tariff 1Wh / 10U16
    327710x80032Partial positive active Energy : Ea+ Tariff 2Wh / 0.001U32
    327730x80051Partial Residual positive active Energy : rEa+ Tariff 2Wh / 10U16
    327740x80062Reserved - -
    327760x80081Reserved - -
    327770x80092Reserved - -
    327790x800B1Reserved - -
    327800x800C2Reserved - -
    327820x800E1Reserved - -
    327830x800F2Reserved - -
    327850x80111Reserved - -
    327860x80122Reserved - -
    327880x80141Reserved - -
    327890x80152Reserved - -
    327910x80171Reserved - -
    327920x80182Partial positive active Energy : Ea- Tariff 1Wh / 0.001U32
    327940x801A1Partial Residual positive active Energy : rEa- Tariff 1Wh / 10U16
    327950x801B2Partial positive active Energy : Ea- Tariff 2Wh / 0.001U32
    327970x801D1Partial Residual positive active Energy : rEa- Tariff 2Wh / 10U16
    327980x801E2Reserved - -
    328000x80201Reserved - -
    328010x80212Reserved - -
    328030x80231Reserved - -
    328040x80242Reserved - -
    328060x80261Reserved - -
    328070x80272Reserved - -
    328090x80291Reserved - -
    328100x802A2Reserved - -
    328120x802C1Reserved - -
    328130x802D2Reserved - -
    328150x802F1Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Energy balance


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    330240x8100Info6NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    330240x81002Net active energy balance (SUM ((Ea+)-(Ea-)) by tariff)Wh / 0.001U32
    330260x81021Net Residual active energy balance (SUM((Ea+)-(Ea-)) by tariff)Wh / 10U16
    330270x81032Net reactive energy balance (SUM((Er+)-(Er-)) by tariff)Wh / 0.001U32
    330290x81051Net Residual reactive energy balance (SUM((Er+)-(Er-)) by tariff)Wh / 10U16

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Line #1


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    202240x4F00Info145NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    202240x4F001Tariff number in progress
    1 : Tariff 1
    2 : Tariff 2
    -U16
    202250x4F012T1 - Ea+Wh / 0.001U32
    202270x4F032T2 - Ea+Wh / 0.001U32
    202290x4F052Reserved - -
    202310x4F072Reserved - -
    202330x4F092Reserved - -
    202350x4F0B2Reserved - -
    202370x4F0D2Reserved - -
    202390x4F0F2Reserved - -
    202410x4F112T1 - Ea-Wh / 0.001U32
    202430x4F132T2 - Ea-Wh / 0.001U32
    202450x4F152Reserved - -
    202470x4F172Reserved - -
    202490x4F192Reserved - -
    202510x4F1B2Reserved - -
    202530x4F1D2Reserved - -
    202550x4F1F2Reserved - -
    202570x4F212Reserved - -
    202590x4F232Reserved - -
    202610x4F252Reserved - -
    202630x4F272Reserved - -
    202650x4F292Reserved - -
    202670x4F2B2Reserved - -
    202690x4F2D2Reserved - -
    202710x4F2F2Reserved - -
    202730x4F312T1 - Er+ (lagging)varh / 0.001U32
    202750x4F332T2 - Er+ (lagging)varh / 0.001U32
    202770x4F352Reserved - -
    202790x4F372Reserved - -
    202810x4F392Reserved - -
    202830x4F3B2Reserved - -
    202850x4F3D2Reserved - -
    202870x4F3F2Reserved - -
    202890x4F412T1 - Er+ (leading)varh / 0.001U32
    202910x4F432T2 - Er+ (leading)varh / 0.001U32
    202930x4F452Reserved - -
    202950x4F472Reserved - -
    202970x4F492Reserved - -
    202990x4F4B2Reserved - -
    203010x4F4D2Reserved - -
    203030x4F4F2Reserved - -
    203050x4F512Reserved - -
    203070x4F532Reserved - -
    203090x4F552Reserved - -
    203110x4F572Reserved - -
    203130x4F592Reserved - -
    203150x4F5B2Reserved - -
    203170x4F5D2Reserved - -
    203190x4F5F2Reserved - -
    203210x4F612T1 - Er- (lagging)varh / 0.001U32
    203230x4F632T2 - Er- (lagging)varh / 0.001U32
    203250x4F652Reserved - -
    203270x4F672Reserved - -
    203290x4F692Reserved - -
    203310x4F6B2Reserved - -
    203330x4F6D2Reserved - -
    203350x4F6F2Reserved - -
    203370x4F712T1 - Er- (leading)varh / 0.001U32
    203390x4F732T2 - Er- (leading)varh / 0.001U32
    203410x4F752Reserved - -
    203430x4F772Reserved - -
    203450x4F792Reserved - -
    203470x4F7B2Reserved - -
    203490x4F7D2Reserved - -
    203510x4F7F2Reserved - -
    203530x4F812T1 - EapVAh / 0.001U32
    203550x4F832T2 - EapVAh / 0.001U32
    203570x4F852Reserved - -
    203590x4F872Reserved - -
    203610x4F892Reserved - -
    203630x4F8B2Reserved - -
    203650x4F8D2Reserved - -
    203670x4F8F2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Line #2


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    222720x5700Info145NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    222720x57001Tariff number in progress
    1 : Tariff 1
    2 : Tariff 2
    -U16
    222730x57012T1 - Ea+Wh / 0.001U32
    222750x57032T2 - Ea+Wh / 0.001U32
    222770x57052Reserved - -
    222790x57072Reserved - -
    222810x57092Reserved - -
    222830x570B2Reserved - -
    222850x570D2Reserved - -
    222870x570F2Reserved - -
    222890x57112T1 - Ea-Wh / 0.001U32
    222910x57132T2 - Ea-Wh / 0.001U32
    222930x57152Reserved - -
    222950x57172Reserved - -
    222970x57192Reserved - -
    222990x571B2Reserved - -
    223010x571D2Reserved - -
    223030x571F2Reserved - -
    223050x57212Reserved - -
    223070x57232Reserved - -
    223090x57252Reserved - -
    223110x57272Reserved - -
    223130x57292Reserved - -
    223150x572B2Reserved - -
    223170x572D2Reserved - -
    223190x572F2Reserved - -
    223210x57312T1 - Er+ (lagging)varh / 0.001U32
    223230x57332T2 - Er+ (lagging)varh / 0.001U32
    223250x57352Reserved - -
    223270x57372Reserved - -
    223290x57392Reserved - -
    223310x573B2Reserved - -
    223330x573D2Reserved - -
    223350x573F2Reserved - -
    223370x57412T1 - Er+ (leading)varh / 0.001U32
    223390x57432T2 - Er+ (leading)varh / 0.001U32
    223410x57452Reserved - -
    223430x57472Reserved - -
    223450x57492Reserved - -
    223470x574B2Reserved - -
    223490x574D2Reserved - -
    223510x574F2Reserved - -
    223530x57512Reserved - -
    223550x57532Reserved - -
    223570x57552Reserved - -
    223590x57572Reserved - -
    223610x57592Reserved - -
    223630x575B2Reserved - -
    223650x575D2Reserved - -
    223670x575F2Reserved - -
    223690x57612T1 - Er- (lagging)varh / 0.001U32
    223710x57632T2 - Er- (lagging)varh / 0.001U32
    223730x57652Reserved - -
    223750x57672Reserved - -
    223770x57692Reserved - -
    223790x576B2Reserved - -
    223810x576D2Reserved - -
    223830x576F2Reserved - -
    223850x57712T1 - Er- (leading)varh / 0.001U32
    223870x57732T2 - Er- (leading)varh / 0.001U32
    223890x57752Reserved - -
    223910x57772Reserved - -
    223930x57792Reserved - -
    223950x577B2Reserved - -
    223970x577D2Reserved - -
    223990x577F2Reserved - -
    224010x57812T1 - EapVAh / 0.001U32
    224030x57832T2 - EapVAh / 0.001U32
    224050x57852Reserved - -
    224070x57872Reserved - -
    224090x57892Reserved - -
    224110x578B2Reserved - -
    224130x578D2Reserved - -
    224150x578F2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Line #3


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    243200x5F00Info145NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    243200x5F001Tariff number in progress
    1 : Tariff 1
    2 : Tariff 2
    -U16
    243210x5F012T1 - Ea+Wh / 0.001U32
    243230x5F032T2 - Ea+Wh / 0.001U32
    243250x5F052Reserved - -
    243270x5F072Reserved - -
    243290x5F092Reserved - -
    243310x5F0B2Reserved - -
    243330x5F0D2Reserved - -
    243350x5F0F2Reserved - -
    243370x5F112T1 - Ea-Wh / 0.001U32
    243390x5F132T2 - Ea-Wh / 0.001U32
    243410x5F152Reserved - -
    243430x5F172Reserved - -
    243450x5F192Reserved - -
    243470x5F1B2Reserved - -
    243490x5F1D2Reserved - -
    243510x5F1F2Reserved - -
    243530x5F212Reserved - -
    243550x5F232Reserved - -
    243570x5F252Reserved - -
    243590x5F272Reserved - -
    243610x5F292Reserved - -
    243630x5F2B2Reserved - -
    243650x5F2D2Reserved - -
    243670x5F2F2Reserved - -
    243690x5F312T1 - Er+ (lagging)varh / 0.001U32
    243710x5F332T2 - Er+ (lagging)varh / 0.001U32
    243730x5F352Reserved - -
    243750x5F372Reserved - -
    243770x5F392Reserved - -
    243790x5F3B2Reserved - -
    243810x5F3D2Reserved - -
    243830x5F3F2Reserved - -
    243850x5F412T1 - Er+ (leading)varh / 0.001U32
    243870x5F432T2 - Er+ (leading)varh / 0.001U32
    243890x5F452Reserved - -
    243910x5F472Reserved - -
    243930x5F492Reserved - -
    243950x5F4B2Reserved - -
    243970x5F4D2Reserved - -
    243990x5F4F2Reserved - -
    244010x5F512Reserved - -
    244030x5F532Reserved - -
    244050x5F552Reserved - -
    244070x5F572Reserved - -
    244090x5F592Reserved - -
    244110x5F5B2Reserved - -
    244130x5F5D2Reserved - -
    244150x5F5F2Reserved - -
    244170x5F612T1 - Er- (lagging)varh / 0.001U32
    244190x5F632T2 - Er- (lagging)varh / 0.001U32
    244210x5F652Reserved - -
    244230x5F672Reserved - -
    244250x5F692Reserved - -
    244270x5F6B2Reserved - -
    244290x5F6D2Reserved - -
    244310x5F6F2Reserved - -
    244330x5F712T1 - Er- (leading)varh / 0.001U32
    244350x5F732T2 - Er- (leading)varh / 0.001U32
    244370x5F752Reserved - -
    244390x5F772Reserved - -
    244410x5F792Reserved - -
    244430x5F7B2Reserved - -
    244450x5F7D2Reserved - -
    244470x5F7F2Reserved - -
    244490x5F812T1 - EapVAh / 0.001U32
    244510x5F832T2 - EapVAh / 0.001U32
    244530x5F852Reserved - -
    244550x5F872Reserved - -
    244570x5F892Reserved - -
    244590x5F8B2Reserved - -
    244610x5F8D2Reserved - -
    244630x5F8F2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Total (1st Part)


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    263680x6700Info33NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    263680x67001Tariff number in progress (available for 1st,2nd and 3rd Parts below)
    1 : Tariff 1
    2 : Tariff 2
    -U16
    263690x67012T1 - Ea+Wh / 0.001U32
    263710x67032T2 - Ea+Wh / 0.001U32
    263730x67052Reserved - -
    263750x67072Reserved - -
    263770x67092Reserved - -
    263790x670B2Reserved - -
    263810x670D2Reserved - -
    263830x670F2Reserved - -
    263850x67112T1 - Ea-Wh / 0.001U32
    263870x67132T2 - Ea-Wh / 0.001U32
    263890x67152Reserved - -
    263910x67172Reserved - -
    263930x67192Reserved - -
    263950x671B2Reserved - -
    263970x671D2Reserved - -
    263990x671F2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Total (2nd Part)


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    264010x6721Info96NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    264010x67212T1 - Er+varh / 0.001U32
    264030x67232T2 - Er+varh / 0.001U32
    264050x67252Reserved - -
    264070x67272Reserved - -
    264090x67292Reserved - -
    264110x672B2Reserved - -
    264130x672D2Reserved - -
    264150x672F2Reserved - -
    264170x67312T1 - Er+ (lagging)varh / 0.001U32
    264190x67332T2 - Er+ (lagging)varh / 0.001U32
    264210x67352Reserved - -
    264230x67372Reserved - -
    264250x67392Reserved - -
    264270x673B2Reserved - -
    264290x673D2Reserved - -
    264310x673F2Reserved - -
    264330x67412T1 - Er+ (leading)varh / 0.001U32
    264350x67432T2 - Er+ (leading)varh / 0.001U32
    264370x67452Reserved - -
    264390x67472Reserved - -
    264410x67492Reserved - -
    264430x674B2Reserved - -
    264450x674D2Reserved - -
    264470x674F2Reserved - -
    264490x67512T1 - Er-varh / 0.001U32
    264510x67532T2 - Er-varh / 0.001U32
    264530x67552Reserved - -
    264550x67572Reserved - -
    264570x67592Reserved - -
    264590x675B2Reserved - -
    264610x675D2Reserved - -
    264630x675F2Reserved - -
    264650x67612T1 - Er- (lagging)varh / 0.001U32
    264670x67632T2 - Er- (lagging)varh / 0.001U32
    264690x67652Reserved - -
    264710x67672Reserved - -
    264730x67692Reserved - -
    264750x676B2Reserved - -
    264770x676D2Reserved - -
    264790x676F2Reserved - -
    264810x67712T1 - Er- (leading)varh / 0.001U32
    264830x67732T2 - Er- (leading)varh / 0.001U32
    264850x67752Reserved - -
    264870x67772Reserved - -
    264890x67792Reserved - -
    264910x677B2Reserved - -
    264930x677D2Reserved - -
    264950x677F2Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Tariff meter (total) - Total (3rd Part)


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    264970x6781Info16NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    264970x67812T1 - EapVAh / 0.001U32
    264990x67832T2 - EapVAh / 0.001U32
    265010x67852Reserved - -
    265030x67872Reserved - -
    265050x67892Reserved - -
    265070x678B2Reserved - -
    265090x678D2Reserved - -
    265110x678F2Reserved - -


    Specific


    + + + + + + + + + + + + + + + + + + + +
    +

    Table Overflow


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    368640x9000Info11NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    368640x90002Value of the meter overflow (10Wh) Wh / 0.1U32
    368660x90021Nb Overflow Total Ea +-U8
    368670x90031Nb Overflow Total Er +-U8
    368680x90041Nb Overflow Total Ea --U8
    368690x90051Nb Overflow Total Er --U8
    368700x90061Nb Overflow Total Eap-U8
    368710x90071Nb Overflow Total Ea T1-U8
    368720x90081Nb Overflow Total Ea T2-U8
    368730x90091Reserved - -
    368740x900A1Reserved - -

    + + + + + + + + + + + + + + + + + + + +
    +

    Current without CT


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    405760x9E80Info6NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    405760x9E802Current: I1A / 1000U32
    405780x9E822Current: I2A / 1000U32
    405800x9E842Current: I3A / 1000U32


    Action


    + + + + + + + + + + + + + + + + + + + +
    +

    Set Tariff


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    404800x9E20Commands2NONEWRITEWRITE

    + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    404800x9E201Reserved - -
    404810x9E211Tariff Number to set-U8

    + + + + + + + + + + + + + + + + + + + +
    +

    Partial Energies Reset


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    405120x9E40Commands1NONEWRITEWRITE

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    405120x9E401Reset
    0x20 : Partial Energies
    -U8_HEX




    Industrialization


    + + + + + + + + + + + + + + + + + + + +
    +

    Indus Mode and MID status


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    405440x9E60Info2NONEREADREAD

    + + + + + + + + + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    405440x9E601Indus Mode (0: indus mode disabled, 1: indus mode enabled)-U8
    405450x9E611MID status (0: non-MID product, 1: MID product-U8

    + + + + + + + + + + + + + + + + + + + +
    +

    COM Board Product ID


    Dec start addressHex start addressTypeSizeLock levelLocked fctsUnlocked fcts
    406080x9EA0Info1NONEREADREAD

    + + + + + + + + + + + + + + + + +
    Dec addressHex addressWords countDescriptionUnitData type
    406080x9EA01Product ID :0x01: E3x/E4x COM Board0x02: E63 COM Board-U8


    + + \ No newline at end of file diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/countise14/Config.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/countise14/Config.java new file mode 100644 index 00000000000..a1a2803e6e9 --- /dev/null +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/countise14/Config.java @@ -0,0 +1,42 @@ +package io.openems.edge.meter.socomec.countise14; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; + +@ObjectClassDefinition( // + name = "Meter SOCOMEC Countis E14", // + description = "Implements the SOCOMEC Countis E14 meter.") +@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 = "Phase", description = "Which Phase is measured by this Meter?") + SinglePhase phase() default SinglePhase.L1; + + @AttributeDefinition(name = "Meter-Type", description = "What is measured by this Meter?") + MeterType type() default MeterType.PRODUCTION; + + @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus brige.") + String modbus_id() default "modbus0"; + + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") + int modbusUnitId(); + + @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 ""; + + String webconsole_configurationFactory_nameHint() default "Meter SOCOMEC Countis E14 [{id}]"; +} \ No newline at end of file diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/countise14/MeterSocomecCountisE14.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/countise14/MeterSocomecCountisE14.java new file mode 100644 index 00000000000..d1622a464d4 --- /dev/null +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/countise14/MeterSocomecCountisE14.java @@ -0,0 +1,150 @@ +package io.openems.edge.meter.socomec.countise14; + +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.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; +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.SignedDoublewordElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; +import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.meter.api.AsymmetricMeter; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; +import io.openems.edge.meter.api.SinglePhaseMeter; +import io.openems.edge.meter.api.SymmetricMeter; + +@Designate(ocd = Config.class, factory = true) +@Component(name = "Meter.SOCOMEC.CountisE24", immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE) +public class MeterSocomecCountisE14 extends AbstractOpenemsModbusComponent + implements SymmetricMeter, AsymmetricMeter, SinglePhaseMeter, OpenemsComponent { + + private MeterType meterType = MeterType.PRODUCTION; + private SinglePhase phase = SinglePhase.L1; + + /* + * Invert power values + */ + private boolean invert = false; + + @Reference + protected ConfigurationAdmin cm; + + public MeterSocomecCountisE14() { + super(// + OpenemsComponent.ChannelId.values(), // + SymmetricMeter.ChannelId.values(), // + AsymmetricMeter.ChannelId.values(), // + SinglePhaseMeter.ChannelId.values(), // + ChannelId.values() // + ); + } + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + @Activate + void activate(ComponentContext context, Config config) { + this.meterType = config.type(); + this.phase = config.phase(); + this.invert = config.invert(); + + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + config.modbus_id()); + + SinglePhaseMeter.initializeCopyPhaseChannel(this, this.phase); + } + + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + ; + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + public Doc doc() { + return this.doc; + } + } + + @Override + public MeterType getMeterType() { + return this.meterType; + } + + @Override + protected ModbusProtocol defineModbusProtocol() { + ModbusProtocol protocol = new ModbusProtocol(this, // + // TODO read "Extended Name" from 0xC38A and verify that this is really a + // Countis E24. Implement the same for all the other SOCOMEC meter. + new FC3ReadRegistersTask(0xc558, Priority.HIGH, // + m(new UnsignedDoublewordElement(0xc558)) // + .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.SCALE_FACTOR_1) // + .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.SCALE_FACTOR_1) // + .build(), // + new DummyRegisterElement(0xc55A, 0xc55D), // + m(SymmetricMeter.ChannelId.FREQUENCY, new UnsignedDoublewordElement(0xc55E)), // + m(new UnsignedDoublewordElement(0xc560)) // + .m(AsymmetricMeter.ChannelId.CURRENT_L1, + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)) // + .m(SymmetricMeter.ChannelId.CURRENT, + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)) // + .build(), // + new DummyRegisterElement(0xc562, 0xc567), // + m(SymmetricMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(0xc568), + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // + m(SymmetricMeter.ChannelId.REACTIVE_POWER, new SignedDoublewordElement(0xc56A), + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)) // + )); + + if (this.invert) { + protocol.addTask(new FC3ReadRegistersTask(0xC86F, Priority.LOW, // + m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedWordElement(0xC86F)), // + new DummyRegisterElement(0xC870), + m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedWordElement(0xC871)) // + )); + } else { + protocol.addTask(new FC3ReadRegistersTask(0xC86F, Priority.LOW, // + m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedWordElement(0xC86F)), // + new DummyRegisterElement(0xC870), + m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedWordElement(0xC871)) // + )); + } + + return protocol; + } + + @Override + public String debugLog() { + return "L:" + this.getActivePower().value().asString(); + } + + @Override + public SinglePhase getPhase() { + return this.getPhase(); + } +} diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/Config.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/Config.java index 2ebee15edaa..02ec5f5ea5f 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/Config.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/Config.java @@ -9,8 +9,14 @@ name = "Meter SOCOMEC Diris A10", // description = "Implements the SOCOMEC Diris A10 meter.") @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?") @@ -21,6 +27,9 @@ @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") int modbusUnitId(); + + @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 ""; diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/MeterSocomecDirisA10.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/MeterSocomecDirisA10.java index e7aa428da52..507e9003ef3 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/MeterSocomecDirisA10.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa10/MeterSocomecDirisA10.java @@ -37,6 +37,11 @@ public class MeterSocomecDirisA10 extends AbstractOpenemsModbusComponent private MeterType meterType = MeterType.PRODUCTION; + /* + * Invert power values + */ + private boolean invert = false; + @Reference protected ConfigurationAdmin cm; @@ -57,8 +62,9 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { this.meterType = config.type(); + this.invert = config.invert(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } @@ -87,9 +93,9 @@ public MeterType getMeterType() { @Override protected ModbusProtocol defineModbusProtocol() { - return new ModbusProtocol(this, // + ModbusProtocol protocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(0xc558, Priority.HIGH, // - cm(new UnsignedDoublewordElement(0xc558)) // + m(new UnsignedDoublewordElement(0xc558)) // .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.SCALE_FACTOR_1) // .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.SCALE_FACTOR_1) // .build(), // @@ -99,32 +105,49 @@ protected ModbusProtocol defineModbusProtocol() { ElementToChannelConverter.SCALE_FACTOR_1), // m(SymmetricMeter.ChannelId.FREQUENCY, new UnsignedDoublewordElement(0xc55E), ElementToChannelConverter.SCALE_FACTOR_1), // - m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0xc560)), // - m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0xc562)), // - m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0xc564)), // - m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0xc566)), // + m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0xc560), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0xc562), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0xc564), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0xc566), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // m(SymmetricMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(0xc568), - ElementToChannelConverter.SCALE_FACTOR_1), // evtl scalefactor -1 !? + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // evtl + // scalefactor + // -1 !? m(SymmetricMeter.ChannelId.REACTIVE_POWER, new SignedDoublewordElement(0xc56A), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // // TODO: add ApparentPower here new DummyRegisterElement(0xc56C, 0xc56F), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new SignedDoublewordElement(0xc570), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new SignedDoublewordElement(0xc572), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L3, new SignedDoublewordElement(0xc574), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L1, new SignedDoublewordElement(0xc576), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L2, new SignedDoublewordElement(0xc578), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L3, new SignedDoublewordElement(0xc57A), - ElementToChannelConverter.SCALE_FACTOR_1) // - ), new FC3ReadRegistersTask(0x0358, Priority.LOW, // - m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0x0358)) // -// m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC708), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)) // )); + + if (this.invert) { + protocol.addTask(new FC3ReadRegistersTask(0x0358, Priority.LOW, // + m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0x0358)) // +// m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC708), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + )); + } else { + protocol.addTask(new FC3ReadRegistersTask(0x0358, Priority.LOW, // + m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0x0358)) // +// m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC708), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + )); + } + + return protocol; } @Override diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/Config.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/Config.java index 4098d2da708..784c0f6f931 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/Config.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/Config.java @@ -9,8 +9,14 @@ name = "Meter SOCOMEC Diris A14", // description = "Implements the SOCOMEC Diris A14 meter.") @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?") @@ -21,6 +27,9 @@ @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") int modbusUnitId(); + + @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 ""; diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/MeterSocomecDirisA14.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/MeterSocomecDirisA14.java index 77d203a5fea..b81dba46df3 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/MeterSocomecDirisA14.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisa14/MeterSocomecDirisA14.java @@ -12,6 +12,7 @@ import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; @@ -22,7 +23,6 @@ import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.taskmanager.Priority; import io.openems.edge.meter.api.AsymmetricMeter; @@ -39,6 +39,11 @@ public class MeterSocomecDirisA14 extends AbstractOpenemsModbusComponent private MeterType meterType = MeterType.PRODUCTION; + /* + * Invert power values + */ + private boolean invert = false; + @Reference protected ConfigurationAdmin cm; @@ -59,8 +64,9 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { this.meterType = config.type(); + this.invert = config.invert(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } @@ -92,9 +98,9 @@ public MeterType getMeterType() { @Override protected ModbusProtocol defineModbusProtocol() { - return new ModbusProtocol(this, // + ModbusProtocol protocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(0xc558, Priority.HIGH, // - cm(new UnsignedDoublewordElement(0xc558)) // + m(new UnsignedDoublewordElement(0xc558)) // .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.SCALE_FACTOR_1) // .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.SCALE_FACTOR_1) // .build(), // @@ -104,39 +110,61 @@ protected ModbusProtocol defineModbusProtocol() { ElementToChannelConverter.SCALE_FACTOR_1), // m(SymmetricMeter.ChannelId.FREQUENCY, new UnsignedDoublewordElement(0xc55E), ElementToChannelConverter.SCALE_FACTOR_1), // - m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0xc560)), // - m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0xc562)), // - m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0xc564)), // - m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0xc566)), // + m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0xc560), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0xc562), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0xc564), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0xc566), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // m(SymmetricMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(0xc568), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(SymmetricMeter.ChannelId.REACTIVE_POWER, new SignedDoublewordElement(0xc56A), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // // TODO: add ApparentPower here new DummyRegisterElement(0xc56C, 0xc56F), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new SignedDoublewordElement(0xc570), - ElementToChannelConverter.SCALE_FACTOR_1), + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new SignedDoublewordElement(0xc572), - ElementToChannelConverter.SCALE_FACTOR_1), + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L3, new SignedDoublewordElement(0xc574), - ElementToChannelConverter.SCALE_FACTOR_1), + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L1, new SignedDoublewordElement(0xc576), - ElementToChannelConverter.SCALE_FACTOR_1), + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L2, new SignedDoublewordElement(0xc578), - ElementToChannelConverter.SCALE_FACTOR_1), + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L3, new SignedDoublewordElement(0xc57A), - ElementToChannelConverter.SCALE_FACTOR_1) // - ), new FC3ReadRegistersTask(0xC702, Priority.LOW, // - m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC702), - ElementToChannelConverter.SCALE_FACTOR_1), // - m(MeterSocomecDirisA14.ChannelId.REACTIVE_PRODUCTION_ENERGY, - new UnsignedDoublewordElement(0xC704), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - new DummyRegisterElement(0xC706, 0xC707), - m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC708), - ElementToChannelConverter.SCALE_FACTOR_1), // - m(MeterSocomecDirisA14.ChannelId.REACTIVE_CONSUMPTION_ENERGY, - new UnsignedDoublewordElement(0xC70A), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)) // )); + + if (this.invert) { + protocol.addTask(new FC3ReadRegistersTask(0xC702, Priority.LOW, // + m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC702), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(MeterSocomecDirisA14.ChannelId.REACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC704), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + new DummyRegisterElement(0xC706, 0xC707), + m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC708), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(MeterSocomecDirisA14.ChannelId.REACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC70A), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + )); + } else { + protocol.addTask(new FC3ReadRegistersTask(0xC702, Priority.LOW, // + m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC702), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(MeterSocomecDirisA14.ChannelId.REACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC704), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + new DummyRegisterElement(0xC706, 0xC707), + m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC708), + ElementToChannelConverter.SCALE_FACTOR_1), // + m(MeterSocomecDirisA14.ChannelId.REACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC70A), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + )); + } + + return protocol; } @Override diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/Config.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/Config.java index d53e8ad95f0..07af52990b0 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/Config.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/Config.java @@ -9,8 +9,14 @@ name = "Meter SOCOMEC Diris B30", // description = "Implements the SOCOMEC Diris B30 meter.") @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?") @@ -22,6 +28,9 @@ @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") int modbusUnitId(); + @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 ""; diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/MeterSocomecDirisB30.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/MeterSocomecDirisB30.java index 51218ad9a05..31187a6d793 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/MeterSocomecDirisB30.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirisb30/MeterSocomecDirisB30.java @@ -37,6 +37,11 @@ public class MeterSocomecDirisB30 extends AbstractOpenemsModbusComponent private MeterType meterType = MeterType.PRODUCTION; + /* + * Invert power values + */ + private boolean invert = false; + @Reference protected ConfigurationAdmin cm; @@ -57,8 +62,9 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { this.meterType = config.type(); + this.invert = config.invert(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } @@ -89,7 +95,7 @@ public MeterType getMeterType() { protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(0x480C, Priority.LOW, // - cm(new UnsignedDoublewordElement(0x480C)) // + m(new UnsignedDoublewordElement(0x480C)) // .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.SCALE_FACTOR_1) // .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.SCALE_FACTOR_1) // .build(), // @@ -98,20 +104,32 @@ protected ModbusProtocol defineModbusProtocol() { m(AsymmetricMeter.ChannelId.VOLTAGE_L3, new UnsignedDoublewordElement(0x4810), ElementToChannelConverter.SCALE_FACTOR_1), // new DummyRegisterElement(0x4812, 0x4819), // - m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0x481A)), // - m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0x481C)), // - m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0x481E)), // - m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0x4820))), // + m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0x481A), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0x481C), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0x481E), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0x4820), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert))), // new FC3ReadRegistersTask(0x482C, Priority.HIGH, // - m(SymmetricMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(0x482C)), // - m(SymmetricMeter.ChannelId.REACTIVE_POWER, new SignedDoublewordElement(0x482E)), // + m(SymmetricMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(0x482C), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(SymmetricMeter.ChannelId.REACTIVE_POWER, new SignedDoublewordElement(0x482E), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // new DummyRegisterElement(0x4830, 0x4837), // - m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new SignedDoublewordElement(0x4838)), // - m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new SignedDoublewordElement(0x483A)), // - m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L3, new SignedDoublewordElement(0x483C)), // - m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L1, new SignedDoublewordElement(0x483E)), // - m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L2, new SignedDoublewordElement(0x4840)), // - m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L3, new SignedDoublewordElement(0x4842)) // + m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new SignedDoublewordElement(0x4838), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new SignedDoublewordElement(0x483A), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L3, new SignedDoublewordElement(0x483C), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L1, new SignedDoublewordElement(0x483E), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L2, new SignedDoublewordElement(0x4840), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L3, new SignedDoublewordElement(0x4842), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)) // )); } diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/Config.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/Config.java index 558262463d7..ce20c36aec8 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/Config.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/Config.java @@ -9,8 +9,14 @@ name = "Meter SOCOMEC Diris E24", // description = "Implements the SOCOMEC Diris E24 meter.") @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?") @@ -22,6 +28,9 @@ @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") int modbusUnitId(); + @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 ""; diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/MeterSocomecDirisE24.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/MeterSocomecDirisE24.java index 66ea0eaad46..3d44a79c396 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/MeterSocomecDirisE24.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/dirise24/MeterSocomecDirisE24.java @@ -37,6 +37,11 @@ public class MeterSocomecDirisE24 extends AbstractOpenemsModbusComponent private MeterType meterType = MeterType.PRODUCTION; + /* + * Invert power values + */ + private boolean invert = false; + @Reference protected ConfigurationAdmin cm; @@ -57,8 +62,9 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { this.meterType = config.type(); + this.invert = config.invert(); - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); } @@ -87,9 +93,9 @@ public MeterType getMeterType() { @Override protected ModbusProtocol defineModbusProtocol() { - return new ModbusProtocol(this, // + ModbusProtocol protocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(0xc558, Priority.HIGH, // - cm(new UnsignedDoublewordElement(0xc558)) // + m(new UnsignedDoublewordElement(0xc558)) // .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.SCALE_FACTOR_1) // .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.SCALE_FACTOR_1) // .build(), // @@ -99,33 +105,54 @@ protected ModbusProtocol defineModbusProtocol() { ElementToChannelConverter.SCALE_FACTOR_1), // m(SymmetricMeter.ChannelId.FREQUENCY, new UnsignedDoublewordElement(0xc55E), ElementToChannelConverter.SCALE_FACTOR_1), // - m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0xc560)), // - m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0xc562)), // - m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0xc564)), // - m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0xc566)), // + m(AsymmetricMeter.ChannelId.CURRENT_L1, new UnsignedDoublewordElement(0xc560), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L2, new UnsignedDoublewordElement(0xc562), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(AsymmetricMeter.ChannelId.CURRENT_L3, new UnsignedDoublewordElement(0xc564), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // + m(SymmetricMeter.ChannelId.CURRENT, new UnsignedDoublewordElement(0xc566), // + ElementToChannelConverter.INVERT_IF_TRUE(this.invert)), // m(SymmetricMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(0xc568), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(SymmetricMeter.ChannelId.REACTIVE_POWER, new SignedDoublewordElement(0xc56A), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // // TODO: add ApparentPower here new DummyRegisterElement(0xc56C, 0xc56F), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L1, new SignedDoublewordElement(0xc570), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L2, new SignedDoublewordElement(0xc572), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.ACTIVE_POWER_L3, new SignedDoublewordElement(0xc574), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L1, new SignedDoublewordElement(0xc576), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L2, new SignedDoublewordElement(0xc578), - ElementToChannelConverter.SCALE_FACTOR_1), // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)), // m(AsymmetricMeter.ChannelId.REACTIVE_POWER_L3, new SignedDoublewordElement(0xc57A), - ElementToChannelConverter.SCALE_FACTOR_1) // -// ), new FC3ReadRegistersTask(0xC702, Priority.LOW, // -// m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC702), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // -// new DummyRegisterElement(0xC704, 0xC707), // PRODUCTION_REACTIVE_ENERGY -// m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC708), ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // + ElementToChannelConverter.SCALE_FACTOR_1_AND_INVERT_IF_TRUE(this.invert)) // )); + + // TODO Check +// if (this.invert) { +// protocol.addTask(new FC3ReadRegistersTask(0xC702, Priority.LOW, // +// m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC702), +// ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // +// new DummyRegisterElement(0xC704, 0xC707), // PRODUCTION_REACTIVE_ENERGY +// m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC708), +// ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // +// )); +// } else { +// protocol.addTask(new FC3ReadRegistersTask(0xC702, Priority.LOW, // +// m(SymmetricMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(0xC702), +// ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // +// new DummyRegisterElement(0xC704, 0xC707), // PRODUCTION_REACTIVE_ENERGY +// m(SymmetricMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(0xC708), +// ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // +// )); +// } + + return protocol; } @Override diff --git a/io.openems.edge.meter.virtual/.classpath b/io.openems.edge.meter.virtual/.classpath new file mode 100644 index 00000000000..3ebd512b99a --- /dev/null +++ b/io.openems.edge.meter.virtual/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/io.openems.edge.meter.virtual/.gitignore b/io.openems.edge.meter.virtual/.gitignore new file mode 100644 index 00000000000..c2b941a96de --- /dev/null +++ b/io.openems.edge.meter.virtual/.gitignore @@ -0,0 +1,2 @@ +/bin_test/ +/generated/ diff --git a/io.openems.edge.meter.virtual/.project b/io.openems.edge.meter.virtual/.project new file mode 100644 index 00000000000..63ff512f7d2 --- /dev/null +++ b/io.openems.edge.meter.virtual/.project @@ -0,0 +1,23 @@ + + + io.openems.edge.meter.virtual + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.edge.meter.virtual/bnd.bnd b/io.openems.edge.meter.virtual/bnd.bnd new file mode 100644 index 00000000000..0c5ae7730f3 --- /dev/null +++ b/io.openems.edge.meter.virtual/bnd.bnd @@ -0,0 +1,29 @@ +Bundle-Name: OpenEMS Edge Meter Virtual Add +Bundle-Vendor: FENECON GmbH +Bundle-Version: 1.0.0.${tstamp} +Bundle-Description: The provider bundle for io.openems.edge.meter.virtual.add. This virtual \ + meter is used to sum up the values from the different components and calculate \ + the average of those values. + +Export-Package: \ + io.openems.edge.meter.api,\ + io.openems.edge.meter.asymmetric.api,\ + io.openems.edge.meter.symmetric.api + +Private-Package: io.openems.edge.meter.virtual.symmetric.add + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.common;version=latest,\ + io.openems.edge.bridge.modbus;version=latest,\ + io.openems.edge.common;version=latest,\ + io.openems.edge.meter.api;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +javac.source: 1.8 +javac.target: 1.8 diff --git a/io.openems.edge.meter.virtual/readme.md b/io.openems.edge.meter.virtual/readme.md new file mode 100644 index 00000000000..5c6b4c94fd0 --- /dev/null +++ b/io.openems.edge.meter.virtual/readme.md @@ -0,0 +1,8 @@ +# io.openems.edge.meter.virtual.add Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.edge.meter.virtual/src/io/openems/edge/meter/virtual/symmetric/add/Config.java b/io.openems.edge.meter.virtual/src/io/openems/edge/meter/virtual/symmetric/add/Config.java new file mode 100644 index 00000000000..a02199b4c8f --- /dev/null +++ b/io.openems.edge.meter.virtual/src/io/openems/edge/meter/virtual/symmetric/add/Config.java @@ -0,0 +1,30 @@ +package io.openems.edge.meter.virtual.symmetric.add; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.edge.meter.api.MeterType; + +@ObjectClassDefinition(// + name = "Meter Virtual Symmetric Add", // + description = "This is a virtual meter which is used to sum up the values from multiple symmetric meters") +@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 = "Grid, Production (=default), Consumption") + MeterType type() default MeterType.PRODUCTION; + + @AttributeDefinition(name = "Meter IDs", description = "Ids of the meters to be summed up") + String[] meterIds(); + + String webconsole_configurationFactory_nameHint() default "Meter Virtual Symmetric Add [{id}]"; + +} diff --git a/io.openems.edge.meter.virtual/src/io/openems/edge/meter/virtual/symmetric/add/VirtualAdd.java b/io.openems.edge.meter.virtual/src/io/openems/edge/meter/virtual/symmetric/add/VirtualAdd.java new file mode 100644 index 00000000000..07ee1117153 --- /dev/null +++ b/io.openems.edge.meter.virtual/src/io/openems/edge/meter/virtual/symmetric/add/VirtualAdd.java @@ -0,0 +1,138 @@ +package io.openems.edge.meter.virtual.symmetric.add; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +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.event.Event; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.metatype.annotations.Designate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.common.channel.calculate.CalculateAverage; +import io.openems.edge.common.channel.calculate.CalculateIntegerSum; +import io.openems.edge.common.channel.calculate.CalculateLongSum; +import io.openems.edge.common.component.AbstractOpenemsComponent; +import io.openems.edge.common.component.ComponentManager; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SymmetricMeter; + +@Designate(ocd = Config.class, factory = true) +@Component(name = "Meter.Virtual.Symmetric.Add", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE, // + property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE// +) // +public class VirtualAdd extends AbstractOpenemsComponent implements SymmetricMeter, OpenemsComponent, EventHandler { + + private final Logger log = LoggerFactory.getLogger(VirtualAdd.class); + + private MeterType meterType = MeterType.PRODUCTION; + + @Reference + protected ComponentManager componentManager; + + private Config config; + + @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MULTIPLE) + private volatile List symmetricMeter = new CopyOnWriteArrayList<>(); + + public VirtualAdd() { + super(// + OpenemsComponent.ChannelId.values(), // + SymmetricMeter.ChannelId.values() // + ); + } + + @Activate + void activate(ComponentContext context, Config config) throws OpenemsNamedException { + super.activate(context, config.id(), config.alias(), config.enabled()); + this.config = config; + } + + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + public void handleEvent(Event event) { + if (!this.isEnabled()) { + return; + } + switch (event.getTopic()) { + case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE: + this.calculateChannelValues(); + } + } + + private void calculateChannelValues() { + // Find all configured SymmetricMeters + List meters = new ArrayList(); + try { + for (String meterId : this.config.meterIds()) { + SymmetricMeter mts = this.componentManager.getComponent(meterId); + meters.add(mts); + } + } catch (Exception e) { + this.logError(this.log, e.getClass().getSimpleName() + ": " + e.getMessage()); + } + + final CalculateAverage meterFrequency = new CalculateAverage(); + final CalculateIntegerSum meterMinActivePower = new CalculateIntegerSum(); + final CalculateIntegerSum meterMaxActivePower = new CalculateIntegerSum(); + final CalculateIntegerSum meterActivePower = new CalculateIntegerSum(); + final CalculateIntegerSum meterReactivePower = new CalculateIntegerSum(); + final CalculateLongSum meterActiveProductionEnergy = new CalculateLongSum(); + final CalculateLongSum meterActiveConsumptionEnergy = new CalculateLongSum(); + final CalculateAverage meterVoltage = new CalculateAverage(); + final CalculateIntegerSum meterCurrent = new CalculateIntegerSum(); + + for (SymmetricMeter meter : meters) { + meterFrequency.addValue(meter.getFrequency()); + meterMinActivePower.addValue(meter.getMinActivePower()); + meterMaxActivePower.addValue(meter.getMaxActivePower()); + meterActivePower.addValue(meter.getActivePower()); + meterReactivePower.addValue(meter.getReactivePower()); + meterActiveConsumptionEnergy.addValue(getActiveConsumptionEnergy()); + meterActiveProductionEnergy.addValue(meter.getActiveProductionEnergy()); + meterVoltage.addValue(meter.getVoltage()); + meterCurrent.addValue(meter.getCurrent()); + } + + this.getFrequency().setNextValue(meterFrequency.calculate()); + this.getMinActivePower().setNextValue(meterMinActivePower.calculate()); + this.getMaxActivePower().setNextValue(meterMaxActivePower.calculate()); + this.getActivePower().setNextValue(meterActivePower.calculate()); + this.getReactivePower().setNextValue(meterReactivePower.calculate()); + this.getActiveConsumptionEnergy().setNextValue(meterActiveConsumptionEnergy.calculate()); + this.getActiveProductionEnergy().setNextValue(meterActiveProductionEnergy.calculate()); + this.getVoltage().setNextValue(meterVoltage.calculate()); + this.getCurrent().setNextValue(meterCurrent.calculate()); + } + + @Override + public MeterType getMeterType() { + return this.meterType; + } + + @Override + public String debugLog() { + return "L:" + this.getActivePower().value().asString(); + } + +} diff --git a/io.openems.edge.meter.virtual/test/.gitignore b/io.openems.edge.meter.virtual/test/.gitignore new file mode 100644 index 00000000000..c2b941a96de --- /dev/null +++ b/io.openems.edge.meter.virtual/test/.gitignore @@ -0,0 +1,2 @@ +/bin_test/ +/generated/ diff --git a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/Config.java b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/Config.java index a9fd7fa5ba4..da6b3ca571e 100644 --- a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/Config.java +++ b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/Config.java @@ -10,8 +10,13 @@ description = "Implements the Weidmueller 525 Energy Meter.") @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?") diff --git a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525.java b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525.java index 95d50cf06f2..6647d7bd069 100644 --- a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525.java +++ b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525.java @@ -65,7 +65,7 @@ public MeterType getMeterType() { protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(102, Priority.HIGH, // - cm(new FloatDoublewordElement(19000)) // + m(new FloatDoublewordElement(19000)) // .m(AsymmetricMeter.ChannelId.VOLTAGE_L1, ElementToChannelConverter.DIRECT_1_TO_1) // .m(SymmetricMeter.ChannelId.VOLTAGE, ElementToChannelConverter.DIRECT_1_TO_1) // .build(), // diff --git a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/RotationField.java b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/RotationField.java index 9258769b67a..fbdd3e20386 100644 --- a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/RotationField.java +++ b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/RotationField.java @@ -1,6 +1,6 @@ package io.openems.edge.meter.weidmueller; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum RotationField implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/WeidmuellerChannelId.java b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/WeidmuellerChannelId.java index 70746afc4f4..baedf5d9df6 100644 --- a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/WeidmuellerChannelId.java +++ b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/WeidmuellerChannelId.java @@ -1,7 +1,7 @@ package io.openems.edge.meter.weidmueller; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; public enum WeidmuellerChannelId implements io.openems.edge.common.channel.ChannelId { diff --git a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Config.java b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Config.java index 66a2dcc452a..0f0115277d0 100644 --- a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Config.java +++ b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Config.java @@ -3,12 +3,17 @@ import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; -@ObjectClassDefinition(name = "PV-Inverter Solar-Log",// +@ObjectClassDefinition(name = "PV-Inverter Solar-Log", // description = "Implements the Solar-Log PV Inverter abstraction.") @interface Config { - + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "pvInverter0"; + @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 = "Modbus-ID", description = "ID of Modbus brige.") @@ -19,10 +24,10 @@ @AttributeDefinition(name = "Maximum Active Power", description = "This is the rated output of the PV installation.") int maxActivePower(); - + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; String webconsole_configurationFactory_nameHint() default "PV-Inverter Solar-Log [{id}]"; - + } diff --git a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PLimitType.java b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PLimitType.java index 09403df5efb..8c9f45a3e4e 100644 --- a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PLimitType.java +++ b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PLimitType.java @@ -1,6 +1,6 @@ package io.openems.edge.pvinverter.solarlog; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum PLimitType implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/SolarLog.java b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/SolarLog.java index d8e584549f0..7caa935c8e1 100644 --- a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/SolarLog.java +++ b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/SolarLog.java @@ -1,7 +1,5 @@ package io.openems.edge.pvinverter.solarlog; -import java.util.function.Consumer; - import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; @@ -16,6 +14,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; +import io.openems.common.exceptions.CheckedConsumer; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.OpenemsType; import io.openems.common.worker.AbstractWorker; @@ -30,10 +32,8 @@ import io.openems.edge.bridge.modbus.api.element.WordOrder; import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC4ReadInputRegistersTask; -import io.openems.edge.common.channel.AccessMode; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.taskmanager.Priority; import io.openems.edge.meter.api.MeterType; @@ -75,7 +75,7 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", + super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); this.maxActivePower = config.maxActivePower(); @@ -160,7 +160,7 @@ public String debugLog() { return "L:" + this.getActivePower().value().asString(); } - public final Consumer setPvLimit = (power) -> { + public final CheckedConsumer setPvLimit = (power) -> { int pLimitPerc = (int) ((double) power / (double) this.maxActivePower * 100.0); // keep percentage in range [0, 100] @@ -182,7 +182,7 @@ public String debugLog() { try { pLimitTypeCh.setNextWriteValue(2); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { log.error("Unable to set pLimitTypeCh: " + e.getMessage()); } }; @@ -223,7 +223,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { .unit(Unit.PERCENT)), P_LIMIT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.KILOWATT)), - WATCH_DOG_TAG(Doc.of(OpenemsType.INTEGER)), // + WATCH_DOG_TAG(Doc.of(OpenemsType.INTEGER) // + .accessMode(AccessMode.READ_WRITE)), // STATUS(Doc.of(Status.values())); private final Doc doc; diff --git a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Status.java b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Status.java index cb2b72a38e6..edc3abd9a1f 100644 --- a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Status.java +++ b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/Status.java @@ -1,6 +1,6 @@ package io.openems.edge.pvinverter.solarlog; -import io.openems.edge.common.channel.OptionsEnum; +import io.openems.common.types.OptionsEnum; public enum Status implements OptionsEnum { UNDEFINED(-1, "Undefined"), // diff --git a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/WatchdogWorker.java b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/WatchdogWorker.java index 0539f837d1d..0d1ef190b04 100644 --- a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/WatchdogWorker.java +++ b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/WatchdogWorker.java @@ -3,7 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.worker.AbstractWorker; public class WatchdogWorker extends AbstractWorker { @@ -22,7 +22,7 @@ public WatchdogWorker(SolarLog parent, int cycleTimeSeconds) { protected void forever() { try { this.parent.getWatchdogTagChannel().setNextWriteValue((int) System.currentTimeMillis()); - } catch (OpenemsException e) { + } catch (OpenemsNamedException e) { this.log.error("Unable to set SolarLog WatchDogTag: " + e.getMessage()); } } diff --git a/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/AllAlphabetically.java b/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/AllAlphabetically.java index 3324b97ae72..9754eb6bb42 100644 --- a/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/AllAlphabetically.java +++ b/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/AllAlphabetically.java @@ -58,7 +58,7 @@ protected synchronized void removeController(Controller controller) { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled(), config.cycleTime()); + super.activate(context, config.id(), config.alias(), config.enabled(), config.cycleTime()); this.controllersIds = config.controllers_ids(); this.updateSortedControllers(); diff --git a/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/Config.java b/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/Config.java index 31fa2be674e..a7a2d3182b1 100644 --- a/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/Config.java +++ b/io.openems.edge.scheduler.allalphabetically/src/io/openems/edge/scheduler/allalphabetically/Config.java @@ -9,8 +9,14 @@ name = "Scheduler All Alphabetically", // description = "This Scheduler takes an ordered list of Component IDs. All remaining Controllers are afterwards ordered alphabetically by their ID.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "scheduler0"; + @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; int cycleTime() default Scheduler.DEFAULT_CYCLE_TIME; diff --git a/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/AllAlphabeticallyTest.java b/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/AllAlphabeticallyTest.java index 85ec8219fcd..b79a42a301b 100644 --- a/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/AllAlphabeticallyTest.java +++ b/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/AllAlphabeticallyTest.java @@ -38,6 +38,11 @@ public String id() { return "scheduler0"; } + @Override + public String alias() { + return ""; + } + @Override public boolean enabled() { return true; diff --git a/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/DummyController.java b/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/DummyController.java index 17c58246e1e..6978e8944d4 100644 --- a/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/DummyController.java +++ b/io.openems.edge.scheduler.allalphabetically/test/io/openems/edge/scheduler/allalphabetically/DummyController.java @@ -10,16 +10,21 @@ public class DummyController implements Controller { private final String id; - + public DummyController(String id) { this.id = id; } - + @Override public String id() { return this.id; } + @Override + public String alias() { + return this.id; + } + @Override public boolean isEnabled() { return true; @@ -44,5 +49,4 @@ public Collection> channels() { public void run() { } - } diff --git a/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/AbstractScheduler.java b/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/AbstractScheduler.java index 460a2334edd..b578a61c183 100644 --- a/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/AbstractScheduler.java +++ b/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/AbstractScheduler.java @@ -13,18 +13,18 @@ protected AbstractScheduler(io.openems.edge.common.channel.ChannelId[] firstInit super(firstInitialChannelIds, furtherInitialChannelIds); } - protected void activate(ComponentContext context, String id, boolean enabled, int cycleTime) { + protected void activate(ComponentContext context, String id, String alias, boolean enabled, int cycleTime) { if (cycleTime < 1) { this.cycleTime = Scheduler.DEFAULT_CYCLE_TIME; } else { this.cycleTime = cycleTime; } - super.activate(context, id, enabled); + super.activate(context, id, alias, enabled); } @Override - protected void activate(ComponentContext context, String id, boolean enabled) { - this.activate(context, id, enabled, Scheduler.DEFAULT_CYCLE_TIME); + protected void activate(ComponentContext context, String id, String alias, boolean enabled) { + this.activate(context, id, alias, enabled, Scheduler.DEFAULT_CYCLE_TIME); } @Override diff --git a/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/Config.java b/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/Config.java index 937ac569d62..907f251fa84 100644 --- a/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/Config.java +++ b/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/Config.java @@ -9,8 +9,14 @@ name = "Scheduler Fixed Order", // description = "This Scheduler takes a list of Component IDs and returns the Controllers statically sorted by this order.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "scheduler0"; + @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; int cycleTime() default Scheduler.DEFAULT_CYCLE_TIME; diff --git a/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/FixedOrder.java b/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/FixedOrder.java index 8a40358677a..c4fb5b01a99 100644 --- a/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/FixedOrder.java +++ b/io.openems.edge.scheduler.fixedorder/src/io/openems/edge/scheduler/fixedorder/FixedOrder.java @@ -82,7 +82,7 @@ void activate(ComponentContext context, Config config) { this.controllersIds = config.controllers_ids(); this.updateSortedControllers(); - super.activate(context, config.id(), config.enabled(), config.cycleTime()); + super.activate(context, config.id(), config.alias(), config.enabled(), config.cycleTime()); // update filter for 'Controller' if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "Controller", diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/BatteryDummy.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/BatteryDummy.java index 0cf4253837f..9c258336ef4 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/BatteryDummy.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/BatteryDummy.java @@ -43,7 +43,7 @@ public BatteryDummy() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); this.disChargeMinVoltage = config.disChargeMinVoltage(); this.chargeMaxVoltage = config.chargeMaxVoltage(); this.disChargeMaxCurrent = config.disChargeMaxCurrent(); @@ -79,6 +79,8 @@ private void updateChannels() { this.getVoltage().setNextValue(this.voltage); this.getMinCellVoltage().setNextValue(this.minCellVoltage); this.getMaxCellVoltage().setNextValue(this.minCellVoltage); + + this.getReadyForWorking().setNextValue(true); } } diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/Config.java index 8927675866a..ad06a2a8c9e 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/battery/Config.java @@ -1,13 +1,20 @@ package io.openems.edge.simulator.battery; +import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition(// name = "Simulator Battery Management System", // description = "Implements a simulated battery management system that sends values given in the configuration") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "bms0"; + @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; int disChargeMinVoltage(); diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/Config.java index ace771e0c04..6a0a0dacebe 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/Config.java @@ -7,8 +7,14 @@ name = "Simulator DataSource: CSV Reader", // description = "This service provides CSV-Input data.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "datasource0"; + @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 = "Factor", description = "Each value in the csv-file is multiplied by this factor.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/CsvDatasource.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/CsvDatasource.java index 30a518a40b8..6a658d0ff01 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/CsvDatasource.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/datasource/csv/CsvDatasource.java @@ -42,7 +42,7 @@ public CsvDatasource() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); this.timeDelta = config.timeDelta(); this.realtime = config.realtime(); diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/Config.java index 748b58a041f..620cce2cd47 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/Config.java @@ -3,12 +3,20 @@ import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.edge.common.sum.GridMode; + @ObjectClassDefinition(// name = "Simulator EssAsymmetric Reacting", // description = "This simulates a 'reacting' asymmetric Energy Storage System.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Datasource-ID", description = "ID of Simulator Datasource.") @@ -23,6 +31,9 @@ @AttributeDefinition(name = "Initial State of Charge [%]") int initialSoc() default 50; + @AttributeDefinition(name = "Grid mode") + GridMode gridMode() default GridMode.ON_GRID; + @AttributeDefinition(name = "Datasource target filter", description = "This is auto-generated by 'Datasource-ID'.") String datasource_target() default ""; diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/EssAsymmetric.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/EssAsymmetric.java index a27dd727818..028c3a57c52 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/EssAsymmetric.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/asymmetric/reacting/EssAsymmetric.java @@ -17,6 +17,7 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.AbstractOpenemsComponent; @@ -79,7 +80,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'datasource' if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "datasource", config.datasource_id())) { @@ -93,6 +94,7 @@ void activate(ComponentContext context, Config config) throws IOException { this.getMaxApparentPower().setNextValue(config.maxApparentPower()); this.getAllowedCharge().setNextValue(this.maxApparentPower * -1); this.getAllowedDischarge().setNextValue(this.maxApparentPower); + this.getGridMode().setNextValue(config.gridMode()); } @Deactivate @@ -186,14 +188,14 @@ public int getPowerPrecision() { } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable(// - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - ManagedSymmetricEss.getModbusSlaveNatureTable(), // - AsymmetricEss.getModbusSlaveNatureTable(), // - ManagedAsymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(EssAsymmetric.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // + AsymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedAsymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(EssAsymmetric.class, accessMode, 300) // .build()); } diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/Config.java index c4bcaa707d5..7519c1cb5c9 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/Config.java @@ -10,13 +10,19 @@ name = "Simulator EssSinglePhase Reacting", // description = "This simulates a 'reacting' single-phase Energy Storage System.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Phase", description = "On which Phase is the ESS connected?") SinglePhase phase() default SinglePhase.L1; - + @AttributeDefinition(name = "Datasource-ID", description = "ID of Simulator Datasource.") String datasource_id() default "datasource0"; diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/EssSinglePhase.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/EssSinglePhase.java index fc770584e53..f3147d9dc45 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/EssSinglePhase.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/singlephase/reacting/EssSinglePhase.java @@ -19,6 +19,7 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.AbstractOpenemsComponent; @@ -81,7 +82,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); this.phase = config.phase(); SinglePhaseEss.initializeCopyPhaseChannel(this, this.phase); @@ -225,12 +226,12 @@ public int getPowerPrecision() { } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable(// - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - ManagedSymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(EssSinglePhase.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(EssSinglePhase.class, accessMode, 300) // .build()); } diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/Config.java index 89bea39b9f8..e50957d1d6e 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/Config.java @@ -9,8 +9,14 @@ name = "Simulator EssSymmetric Reacting", // description = "This simulates a 'reacting' symmetric Energy Storage System.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; + @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 = "Datasource-ID", description = "ID of Simulator Datasource.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/EssSymmetric.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/EssSymmetric.java index 6d212ab920b..3e8aaa3a3bd 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/EssSymmetric.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/ess/symmetric/reacting/EssSymmetric.java @@ -19,6 +19,7 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.AbstractOpenemsComponent; @@ -79,7 +80,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'datasource' if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "datasource", config.datasource_id())) { @@ -188,12 +189,12 @@ public int getPowerPrecision() { } @Override - public ModbusSlaveTable getModbusSlaveTable() { + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { return new ModbusSlaveTable(// - OpenemsComponent.getModbusSlaveNatureTable(), // - SymmetricEss.getModbusSlaveNatureTable(), // - ManagedSymmetricEss.getModbusSlaveNatureTable(), // - ModbusSlaveNatureTable.of(EssSymmetric.class, 300) // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(EssSymmetric.class, accessMode, 300) // .build()); } diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/Config.java index 271445376dd..c06a184fc65 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/Config.java @@ -8,8 +8,13 @@ description = "This simulates a Electric Vehicle Charging Station using data provided by a data source.") @interface Config { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "evcs0"; + @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 = "Datasource-ID", description = "ID of Simulator Datasource.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/SimulatedEvcs.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/SimulatedEvcs.java index 330c443d306..98690d4f291 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/SimulatedEvcs.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/evcs/SimulatedEvcs.java @@ -18,9 +18,9 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; @@ -55,7 +55,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'datasource' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "datasource", config.datasource_id())) { diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/io/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/io/Config.java index f7a3858bde0..31c57018b6f 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/io/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/io/Config.java @@ -8,10 +8,13 @@ description = "Simulates digital input/output channels with name 'InputOutputX', starting with index 0.") @interface Config { - String id() + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "io0"; - default "io0"; + @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 = "Number of input/output channels", description = "This many channels 'InputOutputX' are created.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/io/DigitalInputOutput.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/io/DigitalInputOutput.java index 99fa1b68289..63493c6fecb 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/io/DigitalInputOutput.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/io/DigitalInputOutput.java @@ -11,7 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.edge.common.channel.AccessMode; +import io.openems.common.channel.AccessMode; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.BooleanReadChannel; import io.openems.edge.common.channel.BooleanWriteChannel; @@ -49,7 +49,7 @@ public DigitalInputOutput() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // Generate OutputChannels this.writeChannels = new BooleanWriteChannel[config.numberOfOutputs()]; @@ -57,7 +57,7 @@ void activate(ComponentContext context, Config config) { for (int i = 0; i < config.numberOfOutputs(); i++) { String channelName = String.format(CHANNEL_NAME, i); OpenemsTypeDoc doc = new BooleanDoc() // - .accessMode(AccessMode.WRITE_ONLY); + .accessMode(AccessMode.READ_WRITE); BooleanWriteChannel channel = (BooleanWriteChannel) this.addChannel(new MyChannelId(channelName, doc)); // default to OFF diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/Config.java index 227a0f942c5..488b0821f56 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/Config.java @@ -7,8 +7,14 @@ name = "Simulator GridMeter Acting", // description = "This simulates an 'acting' Grid meter using data provided by a data source.") @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 = "Datasource-ID", description = "ID of Simulator Datasource.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/GridMeter.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/GridMeter.java index 5dbe1f6a6d9..17b85f0d88e 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/GridMeter.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/acting/GridMeter.java @@ -20,9 +20,9 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; @@ -71,7 +71,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'datasource' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "datasource", config.datasource_id())) { diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/Config.java index da3debfdd20..bf0548343ef 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/Config.java @@ -7,8 +7,14 @@ name = "Simulator GridMeter Reacting", // description = "This simulates an 'reacting' Grid meter.") @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 = "Minimum Ever Active Power", description = "This is automatically updated.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/GridMeter.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/GridMeter.java index d2c4b9b1112..20769a205dc 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/GridMeter.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/grid/reacting/GridMeter.java @@ -19,9 +19,9 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; @@ -66,7 +66,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); } @Deactivate diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/Config.java index 71c842ab5d8..eb0dbb7a68b 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/Config.java @@ -7,8 +7,14 @@ name = "Simulator NRCMeter Acting", // description = "This simulates an 'acting' non-regulated-consumption meter using data provided by a data source.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter2"; + @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 = "Datasource-ID", description = "ID of Simulator Datasource.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/NrcMeter.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/NrcMeter.java index d3a9187e40b..57a13f50229 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/NrcMeter.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/nrc/acting/NrcMeter.java @@ -17,9 +17,9 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; @@ -58,7 +58,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'datasource' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "datasource", config.datasource_id())) { diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/Config.java index 55b5c282069..b30edfa88d8 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/Config.java @@ -7,8 +7,14 @@ name = "Simulator ProductionMeter Acting", // description = "This simulates an 'acting' Production meter using data provided by a data source.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter1"; + @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 = "Datasource-ID", description = "ID of Simulator Datasource.") diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/ProductionMeter.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/ProductionMeter.java index 55521f6e0f0..57020704174 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/ProductionMeter.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/meter/production/acting/ProductionMeter.java @@ -17,9 +17,9 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; @@ -58,7 +58,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'datasource' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "datasource", config.datasource_id())) { diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/Config.java index b79ea25cad1..1c015850db7 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/Config.java @@ -1,13 +1,20 @@ package io.openems.edge.simulator.modbus; +import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition(// name = "Simulator Modbus Bridge", // description = "This simulates a Modbus Bridge") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "modbus0"; + @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; String webconsole_configurationFactory_nameHint() default "Simulator Modbus Bridge [{id}]"; diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/ModbusSimulator.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/ModbusSimulator.java index 9b9282f625a..f682303a798 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/ModbusSimulator.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/modbus/ModbusSimulator.java @@ -45,7 +45,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); } @Deactivate @@ -105,4 +105,5 @@ public void addProtocol(String sourceId, ModbusProtocol protocol) { public void removeProtocol(String sourceId) { // ignore } + } diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/Config.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/Config.java index 484bebbf561..63cb509573f 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/Config.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/Config.java @@ -8,8 +8,13 @@ description = "This simulates a PV-Inverter using data provided by a data source.") @interface Config { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "meter1"; + @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 = "Datasource-ID", description = "ID of Simulator Datasource.") @@ -19,5 +24,5 @@ String datasource_target() default ""; String webconsole_configurationFactory_nameHint() default "Simulator PV-Inverter [{id}]"; - + } \ No newline at end of file diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/PvInverter.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/PvInverter.java index 134356004b4..3ca9c8cb3b3 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/PvInverter.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/pvinverter/PvInverter.java @@ -18,9 +18,9 @@ import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; +import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.Unit; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; @@ -59,7 +59,7 @@ public Doc doc() { @Activate void activate(ComponentContext context, Config config) throws IOException { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); // update filter for 'datasource' if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "datasource", config.datasource_id())) { diff --git a/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/Config.java b/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/Config.java index 6a42e399826..7f1fd61ea2c 100644 --- a/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/Config.java +++ b/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/Config.java @@ -7,8 +7,14 @@ name = "Timedata InfluxDB", // description = "This component persists all data to an InfluxDB timeseries database.") @interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "influx0"; + @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 = "IP address", description = "IP address of InfluxDB server.") @@ -26,6 +32,9 @@ @AttributeDefinition(name = "Database", description = "Database name of InfluxDB server.") String database() default "db"; + @AttributeDefinition(name = "Retention-Policy", description = "The InfluxDB retention policy") + String retentionPolicy() default "autogen"; + @AttributeDefinition(name = "Read-Only mode", description = "Activates the read-only mode. Then no data is written to InfluxDB.") boolean isReadOnly() default false; diff --git a/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/InfluxTimedata.java b/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/InfluxTimedata.java index 14120af7e0b..3a04f3fe430 100644 --- a/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/InfluxTimedata.java +++ b/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/InfluxTimedata.java @@ -8,6 +8,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.influxdb.dto.Point; import org.influxdb.dto.Point.Builder; @@ -33,6 +35,7 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.StringUtils; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; @@ -84,9 +87,15 @@ public InfluxTimedata() { @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.enabled()); + super.activate(context, config.id(), config.alias(), config.enabled()); this.influxConnector = new InfluxConnector(config.ip(), config.port(), config.username(), config.password(), - config.database(), config.isReadOnly()); + config.database(), config.retentionPolicy(), config.isReadOnly(), // + (failedPoints, throwable) -> { + String pointsString = StreamSupport.stream(failedPoints.spliterator(), false) + .map(Point::lineProtocol).collect(Collectors.joining(",")); + this.logError(this.log, "Unable to write to InfluxDB: " + throwable.getMessage() + " for " + + StringUtils.toShortString(pointsString, 100)); + }); } @Deactivate diff --git a/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java b/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java index 4f15a9b9ea9..cc70d210f4c 100644 --- a/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java +++ b/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java @@ -10,12 +10,13 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; import org.influxdb.BatchOptions; import org.influxdb.InfluxDB; +import org.influxdb.InfluxDBException; import org.influxdb.InfluxDBFactory; import org.influxdb.InfluxDBIOException; -import org.influxdb.dto.BatchPoints; import org.influxdb.dto.Point; import org.influxdb.dto.Query; import org.influxdb.dto.QueryResult; @@ -45,16 +46,35 @@ public class InfluxConnector { private final String username; private final String password; private final String database; + private final String retentionPolicy; private final boolean isReadOnly; + private final BiConsumer, Throwable> onWriteError; - public InfluxConnector(String ip, int port, String username, String password, String database, boolean isReadOnly) { + /** + * The Constructor. + * + * @param ip IP-Address of the InfluxDB-Server + * @param port Port of the InfluxDB-Server + * @param username The username + * @param password The password + * @param database The database name. If it does not exist, it will be + * created + * @param isReadOnly If true, a 'Read-Only-Mode' is activated, where no data + * is actually written to the database + * @param onWriteError A callback for write-errors, i.e. '(failedPoints, + * throwable) -> {}' + */ + public InfluxConnector(String ip, int port, String username, String password, String database, + String retentionPolicy, boolean isReadOnly, BiConsumer, Throwable> onWriteError) { super(); this.ip = ip; this.port = port; this.username = username; this.password = password; this.database = database; + this.retentionPolicy = retentionPolicy; this.isReadOnly = isReadOnly; + this.onWriteError = onWriteError; } private InfluxDB _influxDB = null; @@ -72,8 +92,16 @@ private InfluxDB getConnection() { if (this._influxDB == null) { InfluxDB influxDB = InfluxDBFactory.connect("http://" + this.ip + ":" + this.port, this.username, this.password); + try { + influxDB.query(new Query("CREATE DATABASE " + this.database, "")); + } catch (InfluxDBException e) { + log.warn("InfluxDB-Exception: " + e.getMessage()); + } influxDB.setDatabase(this.database); - influxDB.enableBatch(BatchOptions.DEFAULTS); + influxDB.setRetentionPolicy(this.retentionPolicy); + influxDB.enableBatch(BatchOptions.DEFAULTS // + .jitterDuration(500) // + .exceptionHandler(this.onWriteError)); this._influxDB = influxDB; } return this._influxDB; @@ -327,31 +355,17 @@ protected static String toChannelAddressStringEnergy(Set channel } /** - * Actually write the BatchPoints to InfluxDB. + * Actually write the Point to InfluxDB. * - * @param batchPoints the InfluxDB BatchPoints + * @param point the InfluxDB Point * @throws OpenemsException on error */ - public void write(BatchPoints batchPoints) throws OpenemsException { + public void write(Point point) throws OpenemsException { if (this.isReadOnly) { log.info("Read-Only-Mode is activated. Not writing points: " - + StringUtils.toShortString(batchPoints.lineProtocol(), 100)); + + StringUtils.toShortString(point.lineProtocol(), 100)); return; } - try { - this.getConnection().write(batchPoints); - } catch (InfluxDBIOException e) { - throw new OpenemsException("Unable to write points: " + e.getMessage()); - } - } - - /** - * Actually write the Point to InfluxDB. - * - * @param point the InfluxDB Point - * @throws OpenemsException on error - */ - public void write(Point point) throws OpenemsException { try { this.getConnection().write(point); } catch (InfluxDBIOException e) { diff --git a/io.openems.wrapper/bnd.bnd b/io.openems.wrapper/bnd.bnd index c74751d09a2..dcf98f2f690 100644 --- a/io.openems.wrapper/bnd.bnd +++ b/io.openems.wrapper/bnd.bnd @@ -9,5 +9,7 @@ com.squareup.okio:okio;version=1.17.2,\ com.squareup.okhttp3:logging-interceptor;version=3.13.1,\ com.squareup.retrofit2:converter-moshi;version=2.5.0,\ - com.squareup.retrofit2:retrofit;version=2.5.0 + com.squareup.retrofit2:retrofit;version=2.5.0,\ + org.dhatim:fastexcel;version=0.10,\ + com.github.rzymek:opczip;version=1.0 \ No newline at end of file diff --git a/io.openems.wrapper/fastexcel.bnd b/io.openems.wrapper/fastexcel.bnd new file mode 100644 index 00000000000..49a1681c4e2 --- /dev/null +++ b/io.openems.wrapper/fastexcel.bnd @@ -0,0 +1,15 @@ +Bundle-Name: fastexcel +Bundle-DocURL: https://github.com/dhatim/fastexcel +Bundle-License: https://opensource.org/licenses/Apache-2.0 +Bundle-Version: 0.10.2 + +Include-Resource: @fastexcel-0.10.2.jar + +-dsannotations: * + +-metatypeannotations: * + +-exportcontents: \ + org.dhatim.fastexcel + +-sources: false diff --git a/io.openems.wrapper/opczip.bnd b/io.openems.wrapper/opczip.bnd new file mode 100644 index 00000000000..beb2b4ac82b --- /dev/null +++ b/io.openems.wrapper/opczip.bnd @@ -0,0 +1,16 @@ +Bundle-Name: opczip +Bundle-Description: Open Package Container ZIP64 streaming implementation +Bundle-DocURL: https://github.com/rzymek/opczip +Bundle-License: https://opensource.org/licenses/Apache-2.0 +Bundle-Version: 1.0.0 + +Include-Resource: @opczip-1.0.0.jar + +-dsannotations: * + +-metatypeannotations: * + +-exportcontents: \ + com.github.rzymek.opczip + +-sources: false diff --git a/ui/package-lock.json b/ui/package-lock.json index 8408e516902..11346f2f952 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,16 +1,16 @@ { "name": "openems-ui", - "version": "2019.2.0", + "version": "2019.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@angular-devkit/architect": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.6.tgz", - "integrity": "sha512-Cg9z4lmCvjt5uD00E/0tBRz3ESjYicmqT3NL/BIsNVNb+s1GwCCoPSOIM8Ss4nyGDtrdono1XKSOmkJnlzF3Cw==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.9.tgz", + "integrity": "sha512-EAFtCs9dsGhpMRC45PoYsrkiExpWz9Ax15qXfzwdDRacz5DmdOVt+QpkLW1beUOwiyj/bhFyj23eaONK2RTn/w==", "dev": true, "requires": { - "@angular-devkit/core": "7.3.6", + "@angular-devkit/core": "7.3.9", "rxjs": "6.3.3" }, "dependencies": { @@ -26,16 +26,16 @@ } }, "@angular-devkit/build-angular": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.13.6.tgz", - "integrity": "sha512-gdP856Z5tuQ69mJla5VPKm/uR86dcrPWYW41Jyu5oIhL2GAQ4JtDEuL2+ppwQO8i8hsXGIAbulGAbrlvU5I8OA==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.13.9.tgz", + "integrity": "sha512-onh07LhdxotDFjja0KKsDWNCwgpM/ymuRr5h0e+vT4AgklP2Uioz1CpzVOgxPIKkdVdGR9QgDinVsWAmY90J8g==", "dev": true, "requires": { - "@angular-devkit/architect": "0.13.6", - "@angular-devkit/build-optimizer": "0.13.6", - "@angular-devkit/build-webpack": "0.13.6", - "@angular-devkit/core": "7.3.6", - "@ngtools/webpack": "7.3.6", + "@angular-devkit/architect": "0.13.9", + "@angular-devkit/build-optimizer": "0.13.9", + "@angular-devkit/build-webpack": "0.13.9", + "@angular-devkit/core": "7.3.9", + "@ngtools/webpack": "7.3.9", "ajv": "6.9.1", "autoprefixer": "9.4.6", "circular-dependency-plugin": "5.0.2", @@ -51,8 +51,8 @@ "loader-utils": "1.2.3", "mini-css-extract-plugin": "0.5.0", "minimatch": "3.0.4", - "node-sass": "4.11.0", - "opn": "5.4.0", + "node-sass": "4.12.0", + "open": "6.0.0", "parse5": "4.0.0", "postcss": "7.0.14", "postcss-import": "12.0.1", @@ -63,7 +63,7 @@ "semver": "5.6.0", "source-map-loader": "0.2.4", "source-map-support": "0.5.10", - "speed-measure-webpack-plugin": "1.3.0", + "speed-measure-webpack-plugin": "1.3.1", "stats-webpack-plugin": "0.7.0", "style-loader": "0.23.1", "stylus": "0.54.5", @@ -78,6 +78,16 @@ "webpack-subresource-integrity": "1.1.0-rc.6" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.9.tgz", + "integrity": "sha512-EAFtCs9dsGhpMRC45PoYsrkiExpWz9Ax15qXfzwdDRacz5DmdOVt+QpkLW1beUOwiyj/bhFyj23eaONK2RTn/w==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.3.9", + "rxjs": "6.3.3" + } + }, "parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", @@ -93,6 +103,16 @@ "tslib": "^1.9.0" } }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", @@ -114,13 +134,45 @@ "buffer-from": "^1.0.0", "source-map": "^0.6.0" } + }, + "webpack": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.0.tgz", + "integrity": "sha512-pxdGG0keDBtamE1mNvT5zyBdx+7wkh6mh7uzMOo/uRQ/fhsdj5FXkh/j5mapzs060forql1oXqXN9HJGju+y7w==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-module-context": "1.7.11", + "@webassemblyjs/wasm-edit": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11", + "acorn": "^6.0.5", + "acorn-dynamic-import": "^4.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.1.0", + "terser-webpack-plugin": "^1.1.0", + "watchpack": "^1.5.0", + "webpack-sources": "^1.3.0" + } } } }, "@angular-devkit/build-optimizer": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.13.6.tgz", - "integrity": "sha512-/EOO0wxw7FIKfove4Fv/aKiKgvH0kFhRT2GOe3pW0yV2y2e1dfQdo2AqdGCfJ4rSbwsfWSGTUUFITc+9jgXFZw==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.13.9.tgz", + "integrity": "sha512-GQtCntthQHSBv5l1ZY5p00JOECb/WcE1qUBo5kFjp84z0fszDkhOy52M1kcWCX4PFzJaY4DKk58hbUE/2UN0jw==", "dev": true, "requires": { "loader-utils": "1.2.3", @@ -144,16 +196,26 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.13.6.tgz", - "integrity": "sha512-lQmYrhOHeBn/r7uw+FfpJUq9puApYe72+mrIfg/UtLwSEYt58n5d156jnuMcfWnCyhYUgYqLTaOUfSM0RV7akw==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.13.9.tgz", + "integrity": "sha512-6ypu6pzNmQxzATF4rTWEhGSl5hyGQ8a/3aCZF/ux+XGc3d4hi2HW+NWlDm1UEna6ZjNtgEPlgfP4q8BKrjRmfA==", "dev": true, "requires": { - "@angular-devkit/architect": "0.13.6", - "@angular-devkit/core": "7.3.6", + "@angular-devkit/architect": "0.13.9", + "@angular-devkit/core": "7.3.9", "rxjs": "6.3.3" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.9.tgz", + "integrity": "sha512-EAFtCs9dsGhpMRC45PoYsrkiExpWz9Ax15qXfzwdDRacz5DmdOVt+QpkLW1beUOwiyj/bhFyj23eaONK2RTn/w==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.3.9", + "rxjs": "6.3.3" + } + }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -166,9 +228,9 @@ } }, "@angular-devkit/core": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.6.tgz", - "integrity": "sha512-aoarMK0DJIdwjVA0OuQIN7b8nKPcF9n5vSMF7MFmhKpTw5/uV3SynQZbm3YCgylu/2CMuiTzKuAunnWWdli//g==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.9.tgz", + "integrity": "sha512-SaxD+nKFW3iCBKsxNR7+66J30EexW/y7tm8m5AvUH+GwSAgIj0ZYmRUzFEPggcaLVA4WnE/YWqIXZMJW5dT7gw==", "requires": { "ajv": "6.9.1", "chokidar": "2.0.4", @@ -177,27 +239,6 @@ "source-map": "0.7.3" }, "dependencies": { - "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -214,18 +255,18 @@ } }, "@angular-devkit/schematics": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.6.tgz", - "integrity": "sha512-YXF7QusmMy3D9H0vNczc1n5BkuEHLwt7cW33euNeGNgTIsD0n6DrUhgClurXicnr2GNPSDYE5+3115lmJkhyrg==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.9.tgz", + "integrity": "sha512-xzROGCYp7aQbeJ3V6YC0MND7wKEAdWqmm/GaCufEk0dDS8ZGe0sQhcM2oBRa2nQqGQNeThFIH51kx+FayrJP0w==", "requires": { - "@angular-devkit/core": "7.3.6", + "@angular-devkit/core": "7.3.9", "rxjs": "6.3.3" }, "dependencies": { "@angular-devkit/core": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.6.tgz", - "integrity": "sha512-aoarMK0DJIdwjVA0OuQIN7b8nKPcF9n5vSMF7MFmhKpTw5/uV3SynQZbm3YCgylu/2CMuiTzKuAunnWWdli//g==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.9.tgz", + "integrity": "sha512-SaxD+nKFW3iCBKsxNR7+66J30EexW/y7tm8m5AvUH+GwSAgIj0ZYmRUzFEPggcaLVA4WnE/YWqIXZMJW5dT7gw==", "requires": { "ajv": "6.9.1", "chokidar": "2.0.4", @@ -234,27 +275,6 @@ "source-map": "0.7.3" } }, - "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -288,84 +308,47 @@ } }, "@angular/cli": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.6.tgz", - "integrity": "sha512-u5lBcYVQRk9cez/DozJvFOYomeko9b5kE+NElyFhPtM3GF1SBcXKb5QyNxH/zSOc850VL7KPe7ZfC6kW3Phhyw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.9.tgz", + "integrity": "sha512-7oJj7CKDlFUbQav1x1CV4xKKcbt0pnxY4unKcm7Q1tVXhu8bU2bc3cDA0aJnbofcYb6TJcd/C2qHgCt78q7edA==", "dev": true, "requires": { - "@angular-devkit/architect": "0.13.6", - "@angular-devkit/core": "7.3.6", - "@angular-devkit/schematics": "7.3.6", - "@schematics/angular": "7.3.6", - "@schematics/update": "0.13.6", + "@angular-devkit/architect": "0.13.9", + "@angular-devkit/core": "7.3.9", + "@angular-devkit/schematics": "7.3.9", + "@schematics/angular": "7.3.9", + "@schematics/update": "0.13.9", "@yarnpkg/lockfile": "1.1.0", "ini": "1.3.5", "inquirer": "6.2.1", "npm-package-arg": "6.1.0", - "opn": "5.4.0", + "open": "6.0.0", "pacote": "9.4.0", "semver": "5.6.0", "symbol-observable": "1.2.0" }, "dependencies": { "@angular-devkit/architect": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.6.tgz", - "integrity": "sha512-Cg9z4lmCvjt5uD00E/0tBRz3ESjYicmqT3NL/BIsNVNb+s1GwCCoPSOIM8Ss4nyGDtrdono1XKSOmkJnlzF3Cw==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.9.tgz", + "integrity": "sha512-EAFtCs9dsGhpMRC45PoYsrkiExpWz9Ax15qXfzwdDRacz5DmdOVt+QpkLW1beUOwiyj/bhFyj23eaONK2RTn/w==", "dev": true, "requires": { - "@angular-devkit/core": "7.3.6", + "@angular-devkit/core": "7.3.9", "rxjs": "6.3.3" } }, - "@angular-devkit/core": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.6.tgz", - "integrity": "sha512-aoarMK0DJIdwjVA0OuQIN7b8nKPcF9n5vSMF7MFmhKpTw5/uV3SynQZbm3YCgylu/2CMuiTzKuAunnWWdli//g==", - "dev": true, - "requires": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - } - }, "@schematics/angular": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.6.tgz", - "integrity": "sha512-Q4VXAjVaCDb2zXFXoIdOfNPsn+EQjqDBHK4a97omytnSNAmu1erl3l2FkEMi6x/VuzK2mQSzBbmHJIgauMmOAA==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.9.tgz", + "integrity": "sha512-B3lytFtFeYNLfWdlrIzvy3ulFRccD2/zkoL0734J+DAGfUz7vbysJ50RwYL46sQUcKdZdvb48ktfu1S8yooP6Q==", "dev": true, "requires": { - "@angular-devkit/core": "7.3.6", - "@angular-devkit/schematics": "7.3.6", + "@angular-devkit/core": "7.3.9", + "@angular-devkit/schematics": "7.3.9", "typescript": "3.2.4" } }, - "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -381,12 +364,6 @@ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, "typescript": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", @@ -413,9 +390,9 @@ } }, "@angular/compiler-cli": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.10.tgz", - "integrity": "sha512-L/mksAkpb8kywYlHlXi5mAyUNNiSHZpoTV+P2t3nNGmqyXuad92TvdZMCO34TYUyGyFz9AVH2qc6LciY9ipQTA==", + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.15.tgz", + "integrity": "sha512-+AsfyKawmj/sa+m4Pz8VSRFbCfx/3IOjAuuEjhopbyr154YpPDSu8NTbcwzq3yfbVcPwK4/4exmbQzpsndaCTg==", "dev": true, "requires": { "canonical-path": "1.0.0", @@ -444,9 +421,9 @@ "dev": true }, "chokidar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", - "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", + "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -460,7 +437,7 @@ "normalize-path": "^3.0.0", "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", - "upath": "^1.1.0" + "upath": "^1.1.1" } }, "cross-spawn": { @@ -489,554 +466,6 @@ "strip-eof": "^1.0.0" } }, - "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -1045,7 +474,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -1066,7 +495,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -1154,6 +583,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "dev": true + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -1207,17 +642,17 @@ } }, "@angular/flex-layout": { - "version": "7.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.23.tgz", - "integrity": "sha512-jH2i3i/M7SbK6scVlj2urVL5OhzwavbQ7KwvUjyc/UwccKnnzuOuWEGCINLja/aoaUO3I35LluCLv6a6VN0olA==", + "version": "7.0.0-beta.24", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.24.tgz", + "integrity": "sha512-ll6sK0nLGxqI/f5+z4jbd+pve1QITzgehv2AuGvfSDgIjPMeqUDB5YZqQmIGM/dQRk/vIio5KCW5LQPJWzMMYQ==", "requires": { "tslib": "^1.7.1" } }, "@angular/forms": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.10.tgz", - "integrity": "sha512-fQccon0Yuni13QJt16npSRlkitPZBLXfWXDFwCEybo/QqtSar3BOJAQFW2yqokrfW5lbO5VDFJ7Pj2dDyBXEAA==", + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.15.tgz", + "integrity": "sha512-p0kcIQLtBBC1qeTA6M3nOuXf/k91E80FKquVM9zEsO2kDjI0oZJVfFYL2UMov5samlJOPN+t6lRHEIUa7ApPsw==", "requires": { "tslib": "^1.9.0" } @@ -1231,9 +666,9 @@ } }, "@angular/language-service": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.10.tgz", - "integrity": "sha512-14mIHbouqD+JauLcNakhHSL7/YB63+w7NrwNJY/DHNT/IyppBUV2dneHHembGEUs3QBY+7oL8FfPCLDRT+K+OQ==", + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.15.tgz", + "integrity": "sha512-Ig5Jr7mnDelaZvSbUd9YhI5am3q1ku9xelAuwvtyDKvQJeKQj3BtTagcOgWrnQBfrJ/FsA/M5Zo48ncSsV0tqQ==", "dev": true }, "@angular/material": { @@ -1261,24 +696,24 @@ } }, "@angular/pwa": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@angular/pwa/-/pwa-0.13.6.tgz", - "integrity": "sha512-2bo8oMliGWr1oaI2UM+54ru9zMcwWcDxJNxD9JE6fA/ar60kZdp/ojmoI5MCME2kqSpy26eZdYYczhB51BQz3A==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular/pwa/-/pwa-0.13.9.tgz", + "integrity": "sha512-UO9LWijwJPVxR+1i+IbDnaHOLAiAypFEwfEioBqqz9LO8sMNUjYmbspKW3myBLosKBaAyB0+wCTSxa3C6zL+ug==", "requires": { - "@angular-devkit/core": "7.3.6", - "@angular-devkit/schematics": "7.3.6", - "@schematics/angular": "7.3.6", + "@angular-devkit/core": "7.3.9", + "@angular-devkit/schematics": "7.3.9", + "@schematics/angular": "7.3.9", "parse5-html-rewriting-stream": "5.1.0", "rxjs": "6.3.3" }, "dependencies": { "@schematics/angular": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.6.tgz", - "integrity": "sha512-Q4VXAjVaCDb2zXFXoIdOfNPsn+EQjqDBHK4a97omytnSNAmu1erl3l2FkEMi6x/VuzK2mQSzBbmHJIgauMmOAA==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.9.tgz", + "integrity": "sha512-B3lytFtFeYNLfWdlrIzvy3ulFRccD2/zkoL0734J+DAGfUz7vbysJ50RwYL46sQUcKdZdvb48ktfu1S8yooP6Q==", "requires": { - "@angular-devkit/core": "7.3.6", - "@angular-devkit/schematics": "7.3.6", + "@angular-devkit/core": "7.3.9", + "@angular-devkit/schematics": "7.3.9", "typescript": "3.2.4" } }, @@ -1323,12 +758,12 @@ } }, "@babel/generator": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz", - "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", + "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", "dev": true, "requires": { - "@babel/types": "^7.3.3", + "@babel/types": "^7.4.0", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", @@ -1364,12 +799,12 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", + "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "^7.4.0" } }, "@babel/highlight": { @@ -1392,37 +827,37 @@ } }, "@babel/parser": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz", - "integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz", + "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==", "dev": true }, "@babel/template": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", - "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", + "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.2.2", - "@babel/types": "^7.2.2" + "@babel/parser": "^7.4.0", + "@babel/types": "^7.4.0" } }, "@babel/traverse": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", - "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz", + "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.2.2", + "@babel/generator": "^7.4.0", "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.2.3", - "@babel/types": "^7.2.2", + "@babel/helper-split-export-declaration": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/types": "^7.4.0", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.10" + "lodash": "^4.17.11" }, "dependencies": { "debug": { @@ -1449,9 +884,9 @@ } }, "@babel/types": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz", - "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", + "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1468,47 +903,48 @@ } }, "@ionic-native/core": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-5.3.0.tgz", - "integrity": "sha512-oIsBvkB/Q63bjKvHGW7zAFMWSXIwnZ/U962UFyuqrF9dKvNSPI5cz8iPOaqKE+jzb2XhlcGKEbp5qAhTI0BcZQ==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-5.5.1.tgz", + "integrity": "sha512-M7ECkZZtAgluVo8rPCyUgnV0Z2SQIcKUk97p6fw+z/ETWU2VRERH8cUpw1v1KpZB8bTlBgYhVmlxwkZo5L3TeQ==", "requires": { "@types/cordova": "^0.0.34" } }, "@ionic-native/splash-screen": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@ionic-native/splash-screen/-/splash-screen-5.3.0.tgz", - "integrity": "sha512-4uSdsvXNZOw8RCGrMb8UJ0l0EWxdCAlBAp/9QVjBwiMMRuBRbEHRyBLVuMb50vJvqYhjXsOGjAX4ExxSAw6ICg==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ionic-native/splash-screen/-/splash-screen-5.5.1.tgz", + "integrity": "sha512-w+EZZsMsuHyD2zkqQrUqyb+RKfnRX7oSlg0gDa2VbAqgqGBG3MGhxmyekjvo6HIImkfRYvZbmgGBLsVw4XILlA==", "requires": { "@types/cordova": "^0.0.34" } }, "@ionic-native/status-bar": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@ionic-native/status-bar/-/status-bar-5.3.0.tgz", - "integrity": "sha512-iKeaETs0FeNq7bnwID+aoDF7gGFOT4BudsM89msIwQz9oqD9qZsB8G3WLqcWHwzeQNX3f6/wRrOQ8GxDCd4CHQ==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ionic-native/status-bar/-/status-bar-5.5.1.tgz", + "integrity": "sha512-oD+qpggnGDtYe7dq6x0bqgDFyRiCTKx4r+gQGj/RCkMys5RSDAXl9clwJ12XzhFqwpwgy5B1RS3AHGtrK1R6iw==", "requires": { "@types/cordova": "^0.0.34" } }, "@ionic/angular": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-4.1.2.tgz", - "integrity": "sha512-GVdtpPekPjAYo/3IiRg/EoVRw1/9RwW07ITW6TRorcX1XOPJlrRXVcQyWFTbHRD9TOikgE1XmMQd/+1Lk0TQFw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-4.4.0.tgz", + "integrity": "sha512-Lk54hBi36mhiPx3uW2GQTyPCpfgpPEiNuSM2oKEhwCwH/OjYPj2TNr/BOQaO5Uk+QDj+Y7J9W7eQPRBKkQgFdQ==", "requires": { - "@ionic/core": "4.1.2", + "@ionic/core": "4.4.0", "tslib": "^1.9.3" } }, "@ionic/angular-toolkit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@ionic/angular-toolkit/-/angular-toolkit-1.4.1.tgz", - "integrity": "sha512-EBXDUPWD53a4upRAtGZDXP24h4FNnZlDyDplNVZOwvTv9+Izg63tPHjl1LkGYm2pPAu+LVgNF/BpNQLk8qbG9g==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@ionic/angular-toolkit/-/angular-toolkit-1.5.1.tgz", + "integrity": "sha512-B5LHuzEv/lgFP7yxfOA3/wggDM/X1mDkWFX8rZtaBRZBbC0vtZwdwDu73RnkfFZ0jTFyIgtoBW8qTrrCyd5MLA==", "dev": true, "requires": { "@schematics/angular": "^7.0.3", "tslib": "^1.9.0", - "typescript": "^3.2.4" + "typescript": "~3.3.3333", + "ws": "^6.1.4" }, "dependencies": { "typescript": { @@ -1516,24 +952,33 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.4000.tgz", "integrity": "sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==", "dev": true + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } } } }, "@ionic/core": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-4.1.2.tgz", - "integrity": "sha512-QVJYpDAycmXgQL3mWdvjlIgrLEG0wVl9+NGZQATEBCTEsuDHrOw7BO28FKPlFCdfTjVA6HDJnSj1HwOUi3Yp4w==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-4.4.0.tgz", + "integrity": "sha512-GKu2qwapAG7dipvBG9RdzSxt/CmQXzO7eIa6Wg6uxuNmGE0WSc6fPZmE0YlFD1dkgvhzSY23XBiWVZMgnH/Wdg==", "requires": { - "ionicons": "4.5.5" + "ionicons": "4.5.6" } }, "@ngtools/webpack": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.6.tgz", - "integrity": "sha512-gDMHybwe8B+1BSvtwM5z4qAkUnZ9b4PTyIVWWECgTLDp2x3WhJi2rMk2X8HTkpbZ52pLPue5GL1sfqlZIOcX7Q==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.9.tgz", + "integrity": "sha512-+ROpqfCXLdQwfP+UNDLk4p959ZrocpStkdd2Iy9CeOJ8yDkityqpstTwQC3oHzzu/95BiyZ0hrHbM6AsPPIvJg==", "dev": true, "requires": { - "@angular-devkit/core": "7.3.6", + "@angular-devkit/core": "7.3.9", "enhanced-resolve": "4.1.0", "rxjs": "6.3.3", "tree-kill": "1.2.1", @@ -1552,17 +997,17 @@ } }, "@ngx-formly/core": { - "version": "5.0.0-rc.7", - "resolved": "https://registry.npmjs.org/@ngx-formly/core/-/core-5.0.0-rc.7.tgz", - "integrity": "sha512-PMjblAG7NX8V5EbrdD0k14RMl12R6PqCDmshBsYVcPzk7Y6BTzj9Ki3gb7LS6v5MiKkah2Ga2NapVd0gBliMHw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ngx-formly/core/-/core-5.1.0.tgz", + "integrity": "sha512-dZAf2P70IT1dFb9LzKqYuvC/aXWBRsn1R5SJQsOvk1pEgxDnBFOyhwzYRud+czuGoKS9PIo+xYXV71Bmdqy1TQ==", "requires": { "tslib": "^1.7.1" } }, "@ngx-formly/ionic": { - "version": "5.0.0-rc.7", - "resolved": "https://registry.npmjs.org/@ngx-formly/ionic/-/ionic-5.0.0-rc.7.tgz", - "integrity": "sha512-xEyBq8riOZWCDPhz9pPlV0VPVADx0B6RTJOrcT2oTt/rjNyjXN92ZazcbYdL+W7DPl22vlSonC8U/xBFQfZLXA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ngx-formly/ionic/-/ionic-5.1.0.tgz", + "integrity": "sha512-nICKanJZ9Qu7A4DoZWZphJSWFu4lAd+HjGyOVAaVNpzbTUCzAaZzYsMq1Qw9BnT7b+p81193WL7kW1IjKy/0ag==", "requires": { "tslib": "^1.9.0" } @@ -1576,16 +1021,54 @@ } }, "@schematics/angular": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.6.tgz", - "integrity": "sha512-Q4VXAjVaCDb2zXFXoIdOfNPsn+EQjqDBHK4a97omytnSNAmu1erl3l2FkEMi6x/VuzK2mQSzBbmHJIgauMmOAA==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.8.tgz", + "integrity": "sha512-7o90bnIxXNpJhWPDY/zCedcG6KMIihz7a4UQe6UdlhEX21MNZLYFiDiR5Vmsx39wjm2EfPh3JTuBIHGmMCXkQQ==", "dev": true, "requires": { - "@angular-devkit/core": "7.3.6", - "@angular-devkit/schematics": "7.3.6", + "@angular-devkit/core": "7.3.8", + "@angular-devkit/schematics": "7.3.8", "typescript": "3.2.4" }, "dependencies": { + "@angular-devkit/core": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.8.tgz", + "integrity": "sha512-3X9uzaZXFpm5o2TSzhD6wEOtVU32CgeytKjD1Scxj+uMMVo48SWLlKiFh312T+smI9ko7tOT8VqxglwYkWosgg==", + "dev": true, + "requires": { + "ajv": "6.9.1", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.8.tgz", + "integrity": "sha512-mvaKoORZIaW/h0VNZ3IQWP0qThRCZRX6869FNlzV0jlW0mhn07XbiIGHCGGSCDRxS7qJ0VbuIVnKXntF+iDeWw==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.3.8", + "rxjs": "6.3.3" + } + }, + "rxjs": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, "typescript": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", @@ -1595,13 +1078,13 @@ } }, "@schematics/update": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.6.tgz", - "integrity": "sha512-TkeigdQTHG40ZGj4CAAzQHh7/rSotg0J6nkBBtc4Y+9md7IGg6dzSFJAvYbDX5JZ9tk7DpukdRHOVVopS/J0AQ==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.9.tgz", + "integrity": "sha512-4MQcaKFxhMzZyE//+DknDh3h3duy3avg2oxSHxdwXlCZ8Q92+4lpegjJcSRiqlEwO4qeJ5XnrjrvzfIiaIZOmA==", "dev": true, "requires": { - "@angular-devkit/core": "7.3.6", - "@angular-devkit/schematics": "7.3.6", + "@angular-devkit/core": "7.3.9", + "@angular-devkit/schematics": "7.3.9", "@yarnpkg/lockfile": "1.1.0", "ini": "1.3.5", "pacote": "9.4.0", @@ -1610,43 +1093,6 @@ "semver-intersect": "1.4.0" }, "dependencies": { - "@angular-devkit/core": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.6.tgz", - "integrity": "sha512-aoarMK0DJIdwjVA0OuQIN7b8nKPcF9n5vSMF7MFmhKpTw5/uV3SynQZbm3YCgylu/2CMuiTzKuAunnWWdli//g==", - "dev": true, - "requires": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - } - }, - "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -1661,19 +1107,13 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true } } }, "@types/chart.js": { - "version": "2.7.48", - "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.48.tgz", - "integrity": "sha512-U8paSPZGkW2WrHf8sgJj7s9MhfRgSz7wfU3CN73JVrcGJ13jGiqiXyr3Bg/4UFl4cbN3S8avHTsbtzYBrnWeVg==" + "version": "2.7.52", + "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.52.tgz", + "integrity": "sha512-h4Md9c0FYPoqHyPeo3sG+wBIDFGz6GubulKUopsmFkSSW2ieyI2phjlj+FjqzTwhrWwR9dbw/HlCW3axj+tWug==" }, "@types/cordova": { "version": "0.0.34", @@ -1704,21 +1144,9 @@ } }, "@types/node": { - "version": "11.11.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.5.tgz", - "integrity": "sha512-pz6wNe/XwyesgfVX7P6B0hY3TnTAYXk6KSTLdpQfbuq3be+hnMoCuFzE+yLTskPdBwmNiGRL2TAsnF09aRugvQ==", - "dev": true - }, - "@types/q": { - "version": "0.0.32", - "resolved": "http://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.14.tgz", - "integrity": "sha512-4GbNCDs98uHCT/OMv40qQC/OpoPbYn9XdXeTiFwHBBFO6eJhYEPUu2zDKirXSbHlvDV8oZ9l8EQ+HrEx/YS9DQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.0.tgz", + "integrity": "sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg==", "dev": true }, "@types/source-list-map": { @@ -1947,9 +1375,9 @@ } }, "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.5", @@ -1973,15 +1401,6 @@ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", "dev": true }, - "adler-32": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz", - "integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=", - "requires": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.1.0" - } - }, "adm-zip": { "version": "0.4.13", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", @@ -2016,7 +1435,6 @@ "version": "6.9.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -2027,14 +1445,12 @@ "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" } } }, @@ -2118,9 +1534,9 @@ } }, "app-root-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", - "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", + "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", "dev": true }, "append-transform": { @@ -2161,6 +1577,16 @@ "sprintf-js": "~1.0.2" } }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -2187,12 +1613,6 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -2219,12 +1639,6 @@ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -2287,6 +1701,12 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, "async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", @@ -2346,6 +1766,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axobject-query": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", + "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -2417,6 +1846,14 @@ "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", + "dev": true + } } }, "babel-template": { @@ -2723,7 +2160,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2760,7 +2197,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2793,20 +2230,20 @@ } }, "browserslist": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.1.tgz", - "integrity": "sha512-/pPw5IAUyqaQXGuD5vS8tcbudyPZ241jk1W5pQBsGDfcjNQt7p8qxZhgMNuygDShte1PibLFexecWUPgmVLfrg==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.6.tgz", + "integrity": "sha512-o/hPOtbU9oX507lIqon+UvPYqpx3mHc8cV3QemSBTXwkG8gSQSK6UKvXcE/DcleU3+A59XTUHyCvZ5qGy8xVAg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000949", - "electron-to-chromium": "^1.3.116", - "node-releases": "^1.1.11" + "caniuse-lite": "^1.0.30000963", + "electron-to-chromium": "^1.3.127", + "node-releases": "^1.1.17" } }, "browserstack": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.1.tgz", - "integrity": "sha512-O8VMT64P9NOLhuIoD4YngyxBURefaSdR4QdhG8l6HZ9VxtU7jc3m6jLufFwKA5gaf7fetfB2TnRJnMxyob+heg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz", + "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==", "dev": true, "requires": { "https-proxy-agent": "^2.2.1" @@ -2814,7 +2251,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2944,9 +2381,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000950", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000950.tgz", - "integrity": "sha512-Cs+4U9T0okW2ftBsCIHuEYXXkki7mjXmjCh4c6PzYShk04qDEr76/iC7KwhLoWoY65wcra1XOsRD+S7BptEb5A==", + "version": "1.0.30000967", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz", + "integrity": "sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==", "dev": true }, "canonical-path": { @@ -2960,17 +2397,6 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, - "cfb": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.1.0.tgz", - "integrity": "sha512-ZqfxNGWTMKhd0a/n6YKJLq8hWbd5kR3cA4kXwUj9vVEdHlwJ09werR8gN15Z7Y1FTXqdD6dE3GGCxv4uc28raA==", - "requires": { - "adler-32": "~1.2.0", - "commander": "^2.16.0", - "crc-32": "~1.2.0", - "printj": "~1.1.2" - } - }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -3065,12 +2491,6 @@ "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", "dev": true }, - "circular-json": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", - "dev": true - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -3168,43 +2588,30 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "codelyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz", - "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.0.1.tgz", + "integrity": "sha512-UVV76+/y1RwaxzCeGPFE3G4GFtfV42r3x8EmRd7XMNFLlLC0ewdtCqWTbvhwPQMxFZZ+OTLEOJNWfyPPn3QFWg==", "dev": true, "requires": { "app-root-path": "^2.1.0", - "css-selector-tokenizer": "^0.7.0", + "aria-query": "^3.0.0", + "axobject-query": "^2.0.2", + "css-selector-tokenizer": "^0.7.1", "cssauron": "^1.4.0", + "damerau-levenshtein": "^1.0.4", "semver-dsl": "^1.0.1", "source-map": "^0.5.7", - "sprintf-js": "^1.1.1" + "sprintf-js": "^1.1.2" }, "dependencies": { "sprintf-js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", - "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true } } }, - "codepage": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.14.0.tgz", - "integrity": "sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k=", - "requires": { - "commander": "~2.14.1", - "exit-on-epipe": "~1.0.1" - }, - "dependencies": { - "commander": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", - "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==" - } - } - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -3230,15 +2637,6 @@ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, - "combine-lists": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", - "dev": true, - "requires": { - "lodash": "^4.5.0" - } - }, "combined-stream": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", @@ -3282,33 +2680,33 @@ "dev": true }, "compressible": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", - "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", + "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", "dev": true, "requires": { - "mime-db": ">= 1.38.0 < 2" + "mime-db": ">= 1.40.0 < 2" }, "dependencies": { "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true } } }, "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dev": true, "requires": { "accepts": "~1.3.5", "bytes": "3.0.0", - "compressible": "~2.0.14", + "compressible": "~2.0.16", "debug": "2.6.9", - "on-headers": "~1.0.1", + "on-headers": "~1.0.2", "safe-buffer": "5.1.2", "vary": "~1.1.2" } @@ -3460,9 +2858,9 @@ } }, "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", + "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==" }, "core-util-is": { "version": "1.0.2", @@ -3493,15 +2891,6 @@ } } }, - "crc-32": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", - "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", - "requires": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.1.0" - } - }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -3514,7 +2903,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3527,7 +2916,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -3947,6 +3336,12 @@ "d3-transition": "1" } }, + "damerau-levenshtein": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", + "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -3961,9 +3356,9 @@ "integrity": "sha512-cqfVLS+346P/Mpj2RpDrBv0P4p2zZhWWvfY5fuWrXNR/K38HaAGEkeOwb47hIpQP9Jr/TIxjZ2/sNMQwdXuGMg==" }, "date-format": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", - "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz", + "integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==", "dev": true }, "date-now": { @@ -4164,7 +3559,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -4254,9 +3649,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.116", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.116.tgz", - "integrity": "sha512-NKwKAXzur5vFCZYBHpdWjTMO8QptNLNP80nItkSIgUOapPAo9Uia+RvkCaZJtO7fhQaVElSvBPWEc2ku6cKsPA==", + "version": "1.3.132", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.132.tgz", + "integrity": "sha512-lDt2+2BAJ8w5NrbC2kTMrLMqW4ttC1re2Z1sayHOy9mW6Pzk1fLCbnnx5L4BzNegFPxbBPwnp0/vQ+J2ybJaAg==", "dev": true }, "elliptic": { @@ -4448,6 +3843,12 @@ "estraverse": "^4.1.1" } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", @@ -4536,67 +3937,6 @@ } } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "exit-on-epipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", - "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" - }, - "expand-braces": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", - "dev": true, - "requires": { - "array-slice": "^0.2.3", - "array-unique": "^0.2.1", - "braces": "^0.1.2" - }, - "dependencies": { - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", - "dev": true, - "requires": { - "expand-range": "^0.1.0" - } - }, - "expand-range": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", - "dev": true, - "requires": { - "is-number": "^0.1.1", - "repeat-string": "^0.2.2" - } - }, - "is-number": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", - "dev": true - }, - "repeat-string": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", - "dev": true - } - } - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -4976,11 +4316,6 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true }, - "frac": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", - "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -5014,6 +4349,17 @@ "null-check": "^1.0.0" } }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs-minipass": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", @@ -5041,13 +4387,13 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz", + "integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==", "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -5066,7 +4412,7 @@ "optional": true }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.5", "bundled": true, "optional": true, "requires": { @@ -5089,7 +4435,7 @@ } }, "chownr": { - "version": "1.0.1", + "version": "1.1.1", "bundled": true, "optional": true }, @@ -5114,15 +4460,15 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "optional": true }, @@ -5165,7 +4511,7 @@ } }, "glob": { - "version": "7.1.2", + "version": "7.1.3", "bundled": true, "optional": true, "requires": { @@ -5183,11 +4529,11 @@ "optional": true }, "iconv-lite": { - "version": "0.4.21", + "version": "0.4.24", "bundled": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { @@ -5244,16 +4590,16 @@ "optional": true }, "minipass": { - "version": "2.2.4", + "version": "2.3.5", "bundled": true, "optional": true, "requires": { - "safe-buffer": "^5.1.1", + "safe-buffer": "^5.1.2", "yallist": "^3.0.0" } }, "minizlib": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "optional": true, "requires": { @@ -5269,32 +4615,38 @@ } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "optional": true }, + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "optional": true + }, "needle": { - "version": "2.2.0", + "version": "2.3.0", "bundled": true, "optional": true, "requires": { - "debug": "^2.1.2", + "debug": "^4.1.0", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.10.0", + "version": "0.12.0", "bundled": true, "optional": true, "requires": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", - "needle": "^2.2.0", + "needle": "^2.2.1", "nopt": "^4.0.1", "npm-packlist": "^1.1.6", "npmlog": "^4.0.2", - "rc": "^1.1.7", + "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", "tar": "^4" @@ -5310,12 +4662,12 @@ } }, "npm-bundled": { - "version": "1.0.3", + "version": "1.0.6", "bundled": true, "optional": true }, "npm-packlist": { - "version": "1.1.10", + "version": "1.4.1", "bundled": true, "optional": true, "requires": { @@ -5382,11 +4734,11 @@ "optional": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", + "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" @@ -5414,15 +4766,15 @@ } }, "rimraf": { - "version": "2.6.2", + "version": "2.6.3", "bundled": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "^7.1.3" } }, "safe-buffer": { - "version": "5.1.1", + "version": "5.1.2", "bundled": true, "optional": true }, @@ -5437,7 +4789,7 @@ "optional": true }, "semver": { - "version": "5.5.0", + "version": "5.7.0", "bundled": true, "optional": true }, @@ -5483,16 +4835,16 @@ "optional": true }, "tar": { - "version": "4.4.1", + "version": "4.4.8", "bundled": true, "optional": true, "requires": { - "chownr": "^1.0.1", + "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", + "safe-buffer": "^5.1.2", "yallist": "^3.0.2" } }, @@ -5502,11 +4854,11 @@ "optional": true }, "wide-align": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { @@ -5515,7 +4867,7 @@ "optional": true }, "yallist": { - "version": "3.0.2", + "version": "3.0.3", "bundled": true, "optional": true } @@ -5669,12 +5021,12 @@ "dev": true }, "handlebars": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", - "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "dev": true, "requires": { - "async": "^2.5.0", + "neo-async": "^2.6.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" @@ -5854,7 +5206,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -5904,7 +5256,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -5975,9 +5327,9 @@ } }, "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", "dev": true }, "iferr": { @@ -6080,9 +5432,9 @@ } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "pkg-dir": { @@ -6196,9 +5548,9 @@ } }, "strip-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.1.0.tgz", - "integrity": "sha512-TjxrkPONqO2Z8QDCpeE2j6n0M6EwxzyDgzEeGp+FbdvaJAt//ClYi6W5my+3ROlC/hZX2KACUwDfK49Ka5eDvg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -6250,9 +5602,9 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "ionicons": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-4.5.5.tgz", - "integrity": "sha512-dIGI73XG6Fg2Ps77ry5Ywe36Pq7wUGkDkl0pBhC4uhsiyoW+oXe+pplmarXEnKEcB5fmlkRrBOxYYzZaoRiUGw==" + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-4.5.6.tgz", + "integrity": "sha512-/F3U/rxdLPGl8eUW5dMouhBBoJuUPrlFO6rkoz4VNuw1lxP6l+bFtsDA8MaVM1dHABp+gIHxtpt+IhwmB3oIfA==" }, "ip": { "version": "1.1.5", @@ -6267,9 +5619,9 @@ "dev": true }, "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", "dev": true }, "is-accessor-descriptor": { @@ -6504,36 +5856,36 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istanbul-api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", - "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.5.tgz", + "integrity": "sha512-meYk1BwDp59Pfse1TvPrkKYgVqAufbdBLEVoqvu/hLLKSaQ054ZTksbNepyc223tMnWdm6AdK2URIJJRqdP87g==", "dev": true, "requires": { "async": "^2.6.1", "compare-versions": "^3.2.1", "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.3", - "istanbul-lib-hook": "^2.0.3", - "istanbul-lib-instrument": "^3.1.0", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.2", - "istanbul-reports": "^2.1.1", - "js-yaml": "^3.12.0", - "make-dir": "^1.3.0", + "istanbul-lib-coverage": "^2.0.4", + "istanbul-lib-hook": "^2.0.6", + "istanbul-lib-instrument": "^3.2.0", + "istanbul-lib-report": "^2.0.7", + "istanbul-lib-source-maps": "^3.0.5", + "istanbul-reports": "^2.2.3", + "js-yaml": "^3.13.0", + "make-dir": "^2.1.0", "minimatch": "^3.0.4", "once": "^1.4.0" }, "dependencies": { "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-LXTBICkMARVgo579kWDm8SqfB6nvSDKNqIOBEjmJRnL04JvoMHCYGWaMddQnseJYtkEuEvO/sIcOxPLk9gERug==", "dev": true }, "istanbul-lib-instrument": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", - "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.2.0.tgz", + "integrity": "sha512-06IM3xShbNW4NgZv5AP4QH0oHqf1/ivFo8eFys0ZjPXHGldHJQWb3riYOKXqmOqfxXBfxu4B+g/iuhOPZH0RJg==", "dev": true, "requires": { "@babel/generator": "^7.0.0", @@ -6541,9 +5893,39 @@ "@babel/template": "^7.0.0", "@babel/traverse": "^7.0.0", "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.3", - "semver": "^5.5.0" + "istanbul-lib-coverage": "^2.0.4", + "semver": "^6.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", + "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", + "dev": true } } }, @@ -6589,9 +5971,9 @@ "dev": true }, "istanbul-lib-hook": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", - "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.6.tgz", + "integrity": "sha512-829DKONApZ7UCiPXcOYWSgkFXa4+vNYoNOt3F+4uDJLKL1OotAoVwvThoEj1i8jmOj7odbYcR3rnaHu+QroaXg==", "dev": true, "requires": { "append-transform": "^1.0.0" @@ -6613,20 +5995,42 @@ } }, "istanbul-lib-report": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", - "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.7.tgz", + "integrity": "sha512-wLH6beJBFbRBLiTlMOBxmb85cnVM1Vyl36N48e4e/aTKSM3WbOx7zbVIH1SQ537fhhsPbX0/C5JB4qsmyRXXyA==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", + "istanbul-lib-coverage": "^2.0.4", + "make-dir": "^2.1.0", "supports-color": "^6.0.0" }, "dependencies": { "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-LXTBICkMARVgo579kWDm8SqfB6nvSDKNqIOBEjmJRnL04JvoMHCYGWaMddQnseJYtkEuEvO/sIcOxPLk9gERug==", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, "supports-color": { @@ -6641,14 +6045,14 @@ } }, "istanbul-lib-source-maps": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", - "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.5.tgz", + "integrity": "sha512-eDhZ7r6r1d1zQPVZehLc3D0K14vRba/eBYkz3rw16DLOrrTzve9RmnkcwrrkWVgO1FL3EK5knujVe5S8QHE9xw==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", + "istanbul-lib-coverage": "^2.0.4", + "make-dir": "^2.1.0", "rimraf": "^2.6.2", "source-map": "^0.6.1" }, @@ -6663,17 +6067,39 @@ } }, "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-LXTBICkMARVgo579kWDm8SqfB6nvSDKNqIOBEjmJRnL04JvoMHCYGWaMddQnseJYtkEuEvO/sIcOxPLk9gERug==", "dev": true }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6683,37 +6109,36 @@ } }, "istanbul-reports": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", - "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.3.tgz", + "integrity": "sha512-T6EbPuc8Cb620LWAYyZ4D8SSn06dY9i1+IgUX2lTH8gbwflMc9Obd33zHTyNX653ybjpamAHS9toKS3E6cGhTw==", "dev": true, "requires": { "handlebars": "^4.1.0" } }, "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.3.1.tgz", + "integrity": "sha512-/vU3/H7U56XsxIXHwgEuWpCgQ0bRi2iiZeUpx7Nqo8n1TpoDHfZhkPIc7CO8I4pnMzYsi3XaSZEiy8cnTfujng==", "dev": true, "requires": { - "exit": "^0.1.2", "glob": "^7.0.6", - "jasmine-core": "~2.8.0" + "jasmine-core": "~3.3.0" }, "dependencies": { "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.3.0.tgz", + "integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==", "dev": true } } }, "jasmine-core": { - "version": "2.99.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", - "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.4.0.tgz", + "integrity": "sha512-HU/YxV4i6GcmiH4duATwAbJQMlE0MsDIR5XmSVxURxKHn3aGAdbY1/ZJFmVRbKtnLwIxxMJD7gYaPsypcbYimg==", "dev": true }, "jasmine-spec-reporter": { @@ -6725,16 +6150,10 @@ "colors": "1.1.2" } }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", - "dev": true - }, "js-base64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz", - "integrity": "sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" }, "js-tokens": { "version": "3.0.2", @@ -6743,21 +6162,13 @@ "dev": true }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - } } }, "jsbn": { @@ -6816,6 +6227,15 @@ } } }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -6834,81 +6254,39 @@ } }, "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.1.tgz", + "integrity": "sha512-iCMBbo4eE5rb1VCpm5qXOAaUiRKRUKiItn8ah2YQQx9qymmSAY98eyQfioChEYcVQLh0zxJ3wS4A0mh90AVPvw==", "dev": true, "requires": { - "core-js": "~2.3.0", - "es6-promise": "~3.0.2", - "lie": "~3.1.0", + "lie": "~3.3.0", "pako": "~1.0.2", - "readable-stream": "~2.0.6" - }, - "dependencies": { - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", - "dev": true - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" } }, "karma": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", - "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.1.0.tgz", + "integrity": "sha512-xckiDqyNi512U4dXGOOSyLKPwek6X/vUizSy2f3geYevbLj+UIdvNwbn7IwfUIL2g1GXEPWt/87qFD1fBbl/Uw==", "dev": true, "requires": { "bluebird": "^3.3.0", "body-parser": "^1.16.1", + "braces": "^2.3.2", "chokidar": "^2.0.3", "colors": "^1.1.0", - "combine-lists": "^1.0.0", "connect": "^3.6.0", "core-js": "^2.2.0", "di": "^0.0.1", "dom-serialize": "^2.2.0", - "expand-braces": "^0.1.1", "flatted": "^2.0.0", "glob": "^7.1.1", "graceful-fs": "^4.1.2", "http-proxy": "^1.13.0", "isbinaryfile": "^3.0.0", - "lodash": "^4.17.5", - "log4js": "^3.0.0", + "lodash": "^4.17.11", + "log4js": "^4.0.0", "mime": "^2.3.1", "minimatch": "^3.0.2", "optimist": "^0.6.1", @@ -6922,10 +6300,16 @@ "useragent": "2.3.0" }, "dependencies": { + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", + "dev": true + }, "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", + "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", "dev": true }, "source-map": { @@ -6963,13 +6347,10 @@ "dev": true }, "karma-jasmine-html-reporter": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", - "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", - "dev": true, - "requires": { - "karma-jasmine": "^1.0.2" - } + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.4.2.tgz", + "integrity": "sha512-7g0gPj8+9JepCNJR9WjDyQ2RkZ375jpdurYQyAYv8PorUCadepl8vrD6LmMqOGcM17cnrynBawQYZHaumgDjBw==", + "dev": true }, "karma-source-map-support": { "version": "1.3.0", @@ -7047,9 +6428,9 @@ } }, "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "requires": { "immediate": "~3.0.5" @@ -7057,7 +6438,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { "graceful-fs": "^4.1.2", @@ -7106,26 +6487,17 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, - "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==" - }, "lodash.tail": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", @@ -7133,22 +6505,22 @@ "dev": true }, "log4js": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", - "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.1.0.tgz", + "integrity": "sha512-eDa+zZPeVEeK6QGJAePyXM6pg4P3n3TO5rX9iZMVY48JshsTyLJZLIL5HipI1kQ2qLsSyOpUqNND/C5H4WhhiA==", "dev": true, "requires": { - "circular-json": "^0.5.5", - "date-format": "^1.2.0", - "debug": "^3.1.0", + "date-format": "^2.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.0", "rfdc": "^1.1.2", - "streamroller": "0.7.0" + "streamroller": "^1.0.4" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" @@ -7239,9 +6611,9 @@ }, "dependencies": { "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", + "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", "dev": true }, "cacache": { @@ -7328,6 +6700,12 @@ } } }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -7613,7 +6991,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -7671,9 +7049,9 @@ "integrity": "sha1-8GPkdHAWJZua2IIVnvwcffCSIc0=" }, "nan": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", - "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==" + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==" }, "nanomatch": { "version": "1.2.13", @@ -7706,9 +7084,9 @@ "dev": true }, "ng2-charts": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-2.0.3.tgz", - "integrity": "sha512-nNm+d6eV4ePRbqA6mdZXBExqT++BifAk+ybkhmYLBiTr+axhyGcX86ojibDdgtiyXiMonUSGbwEnsu9mgl4uwg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-2.2.3.tgz", + "integrity": "sha512-Kxj2bewn537xGFVkR7AgDmfqV+YH4hIL4R36EjlUI9WCWnphzY+VKZGX+D+usXd8e+znuqly+sbGHjddLxupUA==", "requires": { "@types/chart.js": "^2.7.48", "lodash": "^4.17.11", @@ -7817,18 +7195,18 @@ } }, "node-releases": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.11.tgz", - "integrity": "sha512-8v1j5KfP+s5WOTa1spNUAOfreajQPN12JXbRR0oDE+YrJBQCXBnNqUDj27EKpPLOoSiU3tKi3xGPB+JaOdUEQQ==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.18.tgz", + "integrity": "sha512-/mnVgm6u/8OwlIsoyRXtTI0RfQcxZoAZbdwyXap0EeWwcOpDDymyCHM2/aR9XKmHXrvizHoPAOs0pcbiJ6RUaA==", "dev": true, "requires": { "semver": "^5.3.0" } }, "node-sass": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", - "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -7837,12 +7215,10 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", + "lodash": "^4.17.11", "meow": "^3.7.0", "mkdirp": "^0.5.1", - "nan": "^2.10.0", + "nan": "^2.13.2", "node-gyp": "^3.8.0", "npmlog": "^4.0.0", "request": "^2.88.0", @@ -8096,10 +7472,19 @@ "mimic-fn": "^1.0.0" } }, + "open": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.0.0.tgz", + "integrity": "sha512-/yb5mVZBz7mHLySMiSj2DcLtMBbFPJk5JBKEkHVZFxZAPzeg3L026O0T+lbdz1B2nyDnkClRSwRQJdeVUIF7zw==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, "opn": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", - "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", "dev": true, "requires": { "is-wsl": "^1.1.0" @@ -8249,9 +7634,9 @@ }, "dependencies": { "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", + "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", "dev": true }, "cacache": { @@ -8329,9 +7714,9 @@ } }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, "ssri": { @@ -8671,11 +8056,6 @@ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true }, - "printj": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", - "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" - }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -8723,86 +8103,202 @@ } }, "protractor": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.2.tgz", - "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-6.0.0.tgz", + "integrity": "sha512-kCV1hK9gAh+N9to04oXLXtqnnG/UX/aDL2YvLS0SYqzzpeLt8Are3y3JPjWC/OSsYBkOW8T1cXmcMn0QaGUudw==", "dev": true, "requires": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", "blocking-proxy": "^1.0.0", "browserstack": "^1.5.1", "chalk": "^1.1.3", "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", + "jasmine": "^3.3.1", "optimist": "~0.6.0", - "q": "1.4.1", "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", + "selenium-webdriver": "^4.0.0-alpha.1", "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.0.6" + "webdriver-manager": "13.0.0" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "camelcase": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz", + "integrity": "sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" } }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "p-try": "^2.0.0" } }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "p-limit": "^2.0.0" } }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "p-try": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.1.0.tgz", + "integrity": "sha512-H2RyIJ7+A3rjkwKC2l5GGtU4H1vkxKCAGsWasNVd0Set+6i4znxbWy6/j16YDPJDWxhsgZiKAstMEP8wCdSpjA==", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true }, "source-map-support": { @@ -8814,41 +8310,115 @@ "source-map": "^0.5.6" } }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, "webdriver-manager": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz", - "integrity": "sha512-L9TEQmZs6JbMMRQI1w60mfps265/NCr0toYJl7p/R2OAk6oXAfwI6jqYP7EWae+d7Ad2S2Aj4+rzxoSjqk3ZuA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-13.0.0.tgz", + "integrity": "sha512-nwcbUWZwwqw4Nn+Xs8rgyX5JWDJ3oOAEc+OMI9sp8QlM7yg1CDaqqk/2DRZI/ig3DxQwZ1Xna3Eq0unNUaSQvA==", "dev": true, "requires": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" + "adm-zip": "^0.4.13", + "loglevel": "^1.6.1", + "request": "^2.88.0", + "semver": "^5.6.0", + "tar": "^4.4.8", + "xml2js": "^0.4.19", + "yargs": "^12.0.5" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", "dev": true, "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" + "ipaddr.js": "1.9.0" } }, "prr": { @@ -8907,12 +8477,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", @@ -8937,9 +8501,9 @@ "dev": true }, "querystringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", - "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", "dev": true }, "randombytes": { @@ -9324,9 +8888,9 @@ "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" }, "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", "requires": { "tslib": "^1.9.0" } @@ -9426,9 +8990,9 @@ "dev": true }, "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "version": "4.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz", + "integrity": "sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q==", "dev": true, "requires": { "jszip": "^3.1.3", @@ -9552,6 +9116,12 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -9587,7 +9157,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -10066,9 +9636,9 @@ "dev": true }, "readable-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", - "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -10079,9 +9649,9 @@ } }, "speed-measure-webpack-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.0.tgz", - "integrity": "sha512-b9Yd0TrzceMVYSbuamM1sFsGM1oVfyFTM22gOoyLhymNvBVApuYpkdFOgYkKJpN/KhTpcCYcTGHg7X+FJ33Vvw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz", + "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==", "dev": true, "requires": { "chalk": "^2.0.1" @@ -10101,14 +9671,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "ssf": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.10.2.tgz", - "integrity": "sha512-rDhAPm9WyIsY8eZEKyE8Qsotb3j/wBdvMWBUsOhJdfhKGLfQidRjiBUV0y/MkyCLiXQ38FG6LWW/VYUtqlIDZQ==", - "requires": { - "frac": "~1.1.2" - } - }, "sshpk": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", @@ -10216,15 +9778,16 @@ "dev": true }, "streamroller": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.4.tgz", + "integrity": "sha512-Wc2Gm5ygjSX8ZpW9J7Y9FwiSzTlKSvcl0FTTMd3rn7RoxDXpBW+xD9TY5sWL2n0UR61COB0LG1BQvN6nTUQbLQ==", "dev": true, "requires": { - "date-format": "^1.2.0", + "async": "^2.6.1", + "date-format": "^2.0.0", "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "readable-stream": "^2.3.0" + "fs-extra": "^7.0.0", + "lodash": "^4.17.10" }, "dependencies": { "debug": { @@ -10395,9 +9958,9 @@ }, "dependencies": { "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true }, "source-map": { @@ -10435,9 +9998,9 @@ }, "dependencies": { "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", + "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", "dev": true }, "cacache": { @@ -10554,9 +10117,9 @@ } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "pify": { @@ -10585,9 +10148,9 @@ } }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, "source-map": { @@ -10615,7 +10178,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -10751,9 +10314,9 @@ } }, "ts-node": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.0.3.tgz", - "integrity": "sha512-2qayBA4vdtVRuDo11DEFSsD/SFsBXQBRZZhbRGSIkmYmVkWjULn/GGMdG10KVqkaGndljfaTD8dKjWgcejO8YA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.1.0.tgz", + "integrity": "sha512-34jpuOrxDuf+O6iW1JpgTRDFynUZ1iEqtYruBqh35gICNjN8x+LpVcPAcwzLPi9VU6mdA3ym+x233nZmZp445A==", "dev": true, "requires": { "arg": "^4.1.0", @@ -10769,18 +10332,18 @@ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, "tslint": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.14.0.tgz", - "integrity": "sha512-IUla/ieHVnB8Le7LdQFRGlVJid2T/gaJe5VkjzRVSRR6pA2ODYrnfR1hmxi+5+au9l50jBwpbBL34txgv4NnTQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.16.0.tgz", + "integrity": "sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", + "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", "chalk": "^2.3.0", "commander": "^2.12.1", "diff": "^3.2.0", "glob": "^7.1.1", - "js-yaml": "^3.7.0", + "js-yaml": "^3.13.0", "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "resolve": "^1.3.2", @@ -10789,6 +10352,16 @@ "tsutils": "^2.29.0" }, "dependencies": { + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "resolve": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", @@ -10852,20 +10425,20 @@ "dev": true }, "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.6.tgz", + "integrity": "sha512-YDKRX8F0Y+Jr7LhoVk0n4G7ltR3Y7qFAj+DtVBthlOgCcIj1hyMigCfousVfn9HKmvJ+qiFlLDwaHx44/e5ZKw==", "dev": true, "optional": true, "requires": { - "commander": "~2.17.1", + "commander": "~2.20.0", "source-map": "~0.6.1" }, "dependencies": { "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true, "optional": true }, @@ -10934,6 +10507,12 @@ "imurmurhash": "^0.1.4" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -11013,12 +10592,12 @@ } }, "url-parse": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", - "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", "dev": true, "requires": { - "querystringify": "^2.0.0", + "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, @@ -11131,26 +10710,16 @@ "minimalistic-assert": "^1.0.0" } }, - "webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - } - }, "webpack": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.0.tgz", - "integrity": "sha512-pxdGG0keDBtamE1mNvT5zyBdx+7wkh6mh7uzMOo/uRQ/fhsdj5FXkh/j5mapzs060forql1oXqXN9HJGju+y7w==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.31.0.tgz", + "integrity": "sha512-n6RVO3X0LbbipoE62akME9K/JI7qYrwwufs20VvgNNpqUoH4860KkaxJTbGq5bgkVZF9FqyyTG/0WPLH3PVNJA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/wasm-edit": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", "acorn": "^6.0.5", "acorn-dynamic-import": "^4.0.0", "ajv": "^6.1.0", @@ -11166,22 +10735,194 @@ "mkdirp": "~0.5.0", "neo-async": "^2.5.0", "node-libs-browser": "^2.0.0", - "schema-utils": "^0.4.4", + "schema-utils": "^1.0.0", "tapable": "^1.1.0", "terser-webpack-plugin": "^1.1.0", "watchpack": "^1.5.0", "webpack-sources": "^1.3.0" }, "dependencies": { - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true } } }, @@ -11225,9 +10966,9 @@ }, "dependencies": { "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", + "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", "dev": true } } @@ -11400,9 +11141,9 @@ } }, "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", + "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", "dev": true }, "ms": { @@ -11441,9 +11182,9 @@ } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "pump": { @@ -11457,9 +11198,9 @@ } }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, "string-width": { @@ -11655,27 +11396,6 @@ "ultron": "~1.1.0" } }, - "xlsx": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.14.1.tgz", - "integrity": "sha512-7hjB5YuyJo1fuuzXQjwuxD8LSUzE4Rxu5ToC3fB5JSunZxGjLcgKg69bEFG9GYoxeVDx5GL0k1dUodlvaQNRQw==", - "requires": { - "adler-32": "~1.2.0", - "cfb": "^1.1.0", - "codepage": "~1.14.0", - "commander": "~2.17.1", - "crc-32": "~1.2.0", - "exit-on-epipe": "~1.0.1", - "ssf": "~0.10.2" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" - } - } - }, "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", @@ -11783,9 +11503,9 @@ "dev": true }, "yn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.0.0.tgz", - "integrity": "sha512-+Wo/p5VRfxUgBUGy2j/6KX2mj9AYJWOHuhMjMcbBFc3y54o9/4buK1ksBvuiK01C3kby8DH9lSmJdSxw+4G/2Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", + "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", "dev": true }, "zone.js": { diff --git a/ui/package.json b/ui/package.json index 17fd1093d38..baf54daee87 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "openems-ui", - "version": "2019.2.0", + "version": "2019.3.0", "author": "FENECON GmbH", "homepage": "http://openems.io", "scripts": { @@ -19,27 +19,27 @@ "@angular/cdk": "^7.2.2", "@angular/common": "^7.2.2", "@angular/core": "^7.2.2", - "@angular/flex-layout": "7.0.0-beta.23", - "@angular/forms": "^7.2.10", + "@angular/flex-layout": "7.0.0-beta.24", + "@angular/forms": "^7.2.15", "@angular/http": "^7.2.2", "@angular/material": "^7.2.2", "@angular/platform-browser": "^7.2.2", "@angular/platform-browser-dynamic": "^7.2.2", - "@angular/pwa": "^0.13.6", + "@angular/pwa": "^0.13.9", "@angular/router": "^7.2.2", "@angular/service-worker": "^7.2.2", - "@ionic-native/core": "^5.3.0", - "@ionic-native/splash-screen": "^5.3.0", - "@ionic-native/status-bar": "^5.3.0", - "@ionic/angular": "^4.1.2", - "@ngx-formly/core": "^5.0.0-rc.7", - "@ngx-formly/ionic": "^5.0.0-rc.7", + "@ionic-native/core": "^5.5.0", + "@ionic-native/splash-screen": "^5.5.0", + "@ionic-native/status-bar": "^5.5.0", + "@ionic/angular": "^4.4.0", + "@ngx-formly/core": "^5.1.0", + "@ngx-formly/ionic": "^5.1.0", "@ngx-translate/core": "^11.0.1", "angular2-toaster": "7.0.0", "angular2-uuid": "^1.1.1", "chart.js": "^2.8.0", "classlist.js": "^1.1.20150312", - "core-js": "^2.6.4", + "core-js": "^3.0.1", "d3": "5.9.2", "d3-array": "2.0.3", "d3-brush": "1.0.6", @@ -56,41 +56,41 @@ "hammerjs": "2.0.8", "intl": "^1.2.5", "mydaterangepicker": "^4.2.1", - "ng2-charts": "^2.0.3", + "ng2-charts": "^2.2.3", "ng2-cookies": "^1.0.12", "ngx-loading": "^3.0.1", - "node-sass": "^4.11.0", + "node-sass": "^4.12.0", "roboto-fontface": "0.10.0", - "rxjs": "~6.4.0", + "rxjs": "~6.5.2", "semver-compare-multi": "^1.0.3", - "xlsx": "^0.14.1", "zone.js": "~0.8.29" }, "devDependencies": { - "@angular-devkit/architect": "~0.13.6", - "@angular-devkit/build-angular": "~0.13.6", - "@angular-devkit/core": "~7.3.6", - "@angular-devkit/schematics": "~7.3.6", - "@angular/cli": "~7.3.6", + "@angular-devkit/architect": "~0.13.9", + "@angular-devkit/build-angular": "~0.13.9", + "@angular-devkit/core": "~7.3.9", + "@angular-devkit/schematics": "~7.3.9", + "@angular/cli": "~7.3.9", "@angular/compiler": "~7.2.2", - "@angular/compiler-cli": "~7.2.10", - "@angular/language-service": "~7.2.10", - "@ionic/angular-toolkit": "~1.4.1", + "@angular/compiler-cli": "~7.2.15", + "@angular/language-service": "~7.2.15", + "@ionic/angular-toolkit": "~1.5.1", "@types/jasmine": "~3.3.12", "@types/jasminewd2": "~2.0.3", - "@types/node": "~11.11.5", - "codelyzer": "~4.5.0", - "jasmine-core": "~2.99.1", + "@types/node": "~12.0.0", + "codelyzer": "~5.0.1", + "jasmine-core": "~3.4.0", "jasmine-spec-reporter": "~4.2.1", - "karma": "~3.1.4", + "karma": "^4.1.0", "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "~2.0.5", "karma-jasmine": "~1.1.2", - "karma-jasmine-html-reporter": "^0.2.2", - "protractor": "~5.4.0", - "ts-node": "~8.0.3", - "tslint": "~5.14.0", - "typescript": "~3.1.6" + "karma-jasmine-html-reporter": "^1.4.2", + "protractor": "~6.0.0", + "ts-node": "~8.1.0", + "tslint": "~5.16.0", + "typescript": "~3.1.6", + "webpack": "^4.31.0" }, "description": "OpenEMS UI", "cordova": { diff --git a/ui/src/app/about/about.component.html b/ui/src/app/about/about.component.html index e9fb35990c2..deb5535a35c 100644 --- a/ui/src/app/about/about.component.html +++ b/ui/src/app/about/about.component.html @@ -1,16 +1,15 @@ - - - - - - + + + + OpenEMS UI About.UI +

    About.Developed

    diff --git a/ui/src/app/about/about.component.ts b/ui/src/app/about/about.component.ts index a50c8dab56b..bb02fd93ce6 100644 --- a/ui/src/app/about/about.component.ts +++ b/ui/src/app/about/about.component.ts @@ -1,13 +1,22 @@ import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; +import { Edge, Service } from '../shared/shared'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'about', templateUrl: './about.component.html' }) export class AboutComponent { + constructor( - public translate: TranslateService + private translate: TranslateService, + private route: ActivatedRoute, + private service: Service, ) { } + ngOnInit() { + this.service.setCurrentComponent(this.translate.instant('Menu.AboutUI'), this.route); + } + } diff --git a/ui/src/app/app-routing.module.ts b/ui/src/app/app-routing.module.ts index c32d1af15b1..59900ceb156 100644 --- a/ui/src/app/app-routing.module.ts +++ b/ui/src/app/app-routing.module.ts @@ -4,10 +4,11 @@ import { Routes, RouterModule } from '@angular/router'; import { AboutComponent } from './about/about.component'; import { SettingsComponent } from './settings/settings.component'; import { IndexComponent } from './index/index.component'; -import { IndexComponent as EdgeIndexComponent } from './edge/index/index.component'; +import { LiveComponent as EdgeLiveComponent } from './edge/live/live.component'; import { HistoryComponent as EdgeHistoryComponent } from './edge/history/history.component'; import { SettingsComponent as EdgeSettingsComponent } from './edge/settings/settings.component'; import { SystemLogComponent as EdgeSystemLogComponent } from './edge/settings/systemlog/systemlog.component'; +import { ChannelsComponent as EdgeChannelsComponent } from './edge/settings/channels/channels.component'; import { IndexComponent as EdgeComponentInstallIndexComponentComponent } from './edge/settings/component/install/index.component'; import { ComponentInstallComponent as EdgeComponentInstallComponentComponent } from './edge/settings/component/install/install.component'; import { IndexComponent as EdgeComponentUpdateIndexComponentComponent } from './edge/settings/component/update/index.component'; @@ -20,12 +21,13 @@ const routes: Routes = [ { path: 'about', component: AboutComponent }, { path: 'settings', component: SettingsComponent }, - { path: 'device/:edgeId', redirectTo: 'device/:edgeId/index', pathMatch: 'full' }, - { path: 'device/:edgeId/index', component: EdgeIndexComponent }, + { path: 'device/:edgeId', redirectTo: 'device/:edgeId/live', pathMatch: 'full' }, + { path: 'device/:edgeId/live', component: EdgeLiveComponent }, { path: 'device/:edgeId/history', component: EdgeHistoryComponent }, { path: 'device/:edgeId/settings', component: EdgeSettingsComponent }, { path: 'device/:edgeId/settings/systemlog', component: EdgeSystemLogComponent }, + { path: 'device/:edgeId/settings/channels', component: EdgeChannelsComponent }, { path: 'device/:edgeId/settings/component.install', component: EdgeComponentInstallIndexComponentComponent }, { path: 'device/:edgeId/settings/component.install/:factoryId', component: EdgeComponentInstallComponentComponent }, { path: 'device/:edgeId/settings/component.update', component: EdgeComponentUpdateIndexComponentComponent }, diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 07bfccd2d69..cf3ae9906ab 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -1,45 +1,39 @@ - + - -

    - - - + {{ service.currentPageTitle }} + + + + + + + + + + + + + + + General.Live + + + General.History + + + + - - - - @@ -70,9 +64,40 @@

    Index.ConnectionFailed

    + + + + + Menu.Menu + + + + + + Menu.Overview + + + Menu.AboutUI + + + Menu.GeneralSettings + + + + + + - + + +
    \ No newline at end of file diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index a11c6cc9b9b..d65796de187 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -2,12 +2,11 @@ import { Component } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; import { SplashScreen } from '@ionic-native/splash-screen/ngx'; import { StatusBar } from '@ionic-native/status-bar/ngx'; -import { Platform, PopoverController, ToastController } from '@ionic/angular'; +import { Platform, ToastController, MenuController } from '@ionic/angular'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; import { environment } from '../environments'; -import { PopoverPage } from './shared/popover/popover.component'; -import { Service, Websocket } from './shared/shared'; +import { Service, Websocket, Edge } from './shared/shared'; import { LanguageTag } from './shared/translate/language'; @Component({ @@ -17,6 +16,9 @@ import { LanguageTag } from './shared/translate/language'; export class AppComponent { public env = environment; public backUrl: string | boolean = '/'; + public enableSideMenu: boolean; + public isEdgeIndexPage: boolean = false; + public isSystemLogEnabled: boolean = false; private ngUnsubscribe: Subject = new Subject(); @@ -26,9 +28,9 @@ export class AppComponent { private statusBar: StatusBar, public websocket: Websocket, public service: Service, - private popoverController: PopoverController, public router: Router, - public toastController: ToastController + public toastController: ToastController, + public menu: MenuController, ) { // this.initializeApp(); service.setLang(LanguageTag.DE); @@ -52,37 +54,69 @@ export class AppComponent { }); toast.present(); }); - // set initial backUrl - this.updateBackUrl(window.location.pathname); + // set inital URL + this.updateUrl(window.location.pathname); // update backUrl on navigation events this.router.events.pipe( takeUntil(this.ngUnsubscribe), filter(event => event instanceof NavigationEnd) ).subscribe(event => { - let url = (event).urlAfterRedirects; - this.updateBackUrl(url); + this.updateUrl((event).urlAfterRedirects); }) } + updateUrl(url: string) { + this.updateBackUrl(url); + this.updateEnableSideMenu(url); + this.updateIsEdgeIndexPage(url); + } + + updateEnableSideMenu(url: string) { + let urlArray = url.split('/'); + let file = urlArray.pop(); + + if (file == 'settings' || file == 'about' || urlArray.length > 3) { + // disable side-menu; show back-button instead + this.enableSideMenu = false; + } else { + // enable side-menu if back-button is not needed + this.enableSideMenu = true; + } + } + updateBackUrl(url: string) { - // disable backUrl on initial 'index' page + // disable backUrl & Segment Navigation on initial 'index' page if (url === '/index') { this.backUrl = false; return; } + // set backUrl for general settings when an Edge had been selected before + let currentEdge: Edge = this.service.currentEdge.value; + if (url === '/settings' && currentEdge != null) { + this.backUrl = '/device/' + currentEdge.id + "/live" + return; + } + let urlArray = url.split('/'); let backUrl: string | boolean = '/'; let file = urlArray.pop(); + // disable backUrl for History & EdgeIndex Component ++ Enable Segment Navigation + if ((file == 'history' || file == 'live') && urlArray.length == 3) { + this.backUrl = false; + return; + } else { + } + // disable backUrl to first 'index' page from Edge index if there is only one Edge in the system - if (file === 'index' && urlArray.length == 3 && this.env.backend === "OpenEMS Edge") { + if (file === 'live' && urlArray.length == 3 && this.env.backend === "OpenEMS Edge") { this.backUrl = false; return; } // remove one part of the url for 'index' - if (file === 'index') { + if (file === 'live') { urlArray.pop(); } // re-join the url @@ -95,19 +129,20 @@ export class AppComponent { this.backUrl = backUrl; } + updateIsEdgeIndexPage(url: string) { + let urlArray = url.split('/'); + let file = urlArray.pop(); + + // Enable Segment Navigation for Edge-Index-Page + if ((file == 'history' || file == 'live') && urlArray.length == 3) { + this.isEdgeIndexPage = true; + } else { + this.isEdgeIndexPage = false; + } + } + ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); } - - //Presents Popovermenu for Navbar - async presentPopover(event: any) { - const popover = await this.popoverController.create({ - component: PopoverPage, - event: event, - translucent: false - }); - return await popover.present(); - } - } diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 460f7957cb8..d65eb2b6830 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -15,6 +15,7 @@ import { EdgeModule } from './edge/edge.module'; // components import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; +import { SystemLogComponent } from './edge/settings/systemlog/systemlog.component'; // services import { Language } from './shared/translate/language'; @@ -23,8 +24,6 @@ import { Language } from './shared/translate/language'; import { LOCALE_ID } from '@angular/core'; import { registerLocaleData } from '@angular/common'; import localDE from '@angular/common/locales/de'; -import { PopoverPage } from './shared/popover/popover.component'; -import { PopoverPageModule } from './shared/popover/popover.module'; import { SettingsModule } from './settings/settings.module'; import { SettingsModule as EdgeSettingsModule } from './edge/settings/settings.module'; import { RouteReuseStrategy } from '@angular/router'; @@ -32,15 +31,15 @@ import { ServiceWorkerModule } from '@angular/service-worker'; import { environment as env } from '../environments/environment'; import { FormlyModule } from '@ngx-formly/core'; import { RepeatTypeComponent } from './edge/settings/component/shared/repeat'; -import { EvcsModalPageModule } from './edge/index/widget/evcs/evcs-modal/evcs-modal.module'; - +import { EvcsModalPageModule } from './edge/live/widgets/evcs/evcs-modal/evcs-modal.module'; @NgModule({ declarations: [ AppComponent, - RepeatTypeComponent + RepeatTypeComponent, + SystemLogComponent ], - entryComponents: [PopoverPage], + entryComponents: [], imports: [ BrowserModule, IonicModule.forRoot(), @@ -60,7 +59,6 @@ import { EvcsModalPageModule } from './edge/index/widget/evcs/evcs-modal/evcs-mo TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: Language } }), - PopoverPageModule, env.production && env.backend == "OpenEMS Backend" ? ServiceWorkerModule.register('ngsw-worker.js', { enabled: true }) : [], ], providers: [ diff --git a/ui/src/app/edge/edge.module.ts b/ui/src/app/edge/edge.module.ts index 3363b58e01b..45dd157d833 100644 --- a/ui/src/app/edge/edge.module.ts +++ b/ui/src/app/edge/edge.module.ts @@ -1,12 +1,12 @@ import { NgModule } from '@angular/core'; import { SharedModule } from './../shared/shared.module'; import { HistoryModule } from './history/history.module'; -import { IndexModule } from './index/index.module'; +import { LiveModule } from './live/live.module'; @NgModule({ imports: [ SharedModule, - IndexModule, + LiveModule, HistoryModule ] }) diff --git a/ui/src/app/edge/history/chart/energy/energy.component.ts b/ui/src/app/edge/history/chart/energy/energy.component.ts index fc5ade9414f..83d896c2fb9 100644 --- a/ui/src/app/edge/history/chart/energy/energy.component.ts +++ b/ui/src/app/edge/history/chart/energy/energy.component.ts @@ -60,7 +60,7 @@ export class EnergyComponent extends AbstractHistoryChart implements OnChanges { }]; ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route); let options = Utils.deepCopy(DEFAULT_TIME_CHART_OPTIONS); options.scales.yAxes[0].scaleLabel.labelString = "kW"; options.tooltips.callbacks.label = function (tooltipItem: TooltipItem, data: Data) { diff --git a/ui/src/app/edge/history/chart/soc/soc.component.ts b/ui/src/app/edge/history/chart/soc/soc.component.ts index b4895799ecd..a07f2b02f89 100644 --- a/ui/src/app/edge/history/chart/soc/soc.component.ts +++ b/ui/src/app/edge/history/chart/soc/soc.component.ts @@ -49,7 +49,7 @@ export class SocComponent extends AbstractHistoryChart implements OnInit, OnChan }]; ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route); let options = Utils.deepCopy(DEFAULT_TIME_CHART_OPTIONS); options.scales.yAxes[0].scaleLabel.labelString = this.translate.instant('General.Percentage'); options.tooltips.callbacks.label = function (tooltipItem: TooltipItem, data: Data) { diff --git a/ui/src/app/edge/history/chart/widget/channelthreshold/channelthreshold.component.ts b/ui/src/app/edge/history/chart/widget/channelthreshold/channelthreshold.component.ts index 3f673d46245..05172ec3e79 100644 --- a/ui/src/app/edge/history/chart/widget/channelthreshold/channelthreshold.component.ts +++ b/ui/src/app/edge/history/chart/widget/channelthreshold/channelthreshold.component.ts @@ -48,7 +48,7 @@ export class ChannelthresholdComponent extends AbstractHistoryChart implements O }]; ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route); let options = Utils.deepCopy(DEFAULT_TIME_CHART_OPTIONS); options.scales.yAxes[0].scaleLabel.labelString = "%"; options.tooltips.callbacks.label = function (tooltipItem: TooltipItem, data: Data) { diff --git a/ui/src/app/edge/history/chart/widget/evcs/evcs.component.ts b/ui/src/app/edge/history/chart/widget/evcs/evcs.component.ts index e8d0bc883d6..6ba225b960c 100644 --- a/ui/src/app/edge/history/chart/widget/evcs/evcs.component.ts +++ b/ui/src/app/edge/history/chart/widget/evcs/evcs.component.ts @@ -40,7 +40,7 @@ export class EvcsComponent extends AbstractHistoryChart implements OnInit, OnCha }]; ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route); let options = Utils.deepCopy(DEFAULT_TIME_CHART_OPTIONS); options.scales.yAxes[0].scaleLabel.labelString = "kW"; options.tooltips.callbacks.label = function (tooltipItem: TooltipItem, data: Data) { diff --git a/ui/src/app/edge/history/chart/widget/widget.component.ts b/ui/src/app/edge/history/chart/widget/widget.component.ts index 4d09024c662..c53ff8fb84d 100644 --- a/ui/src/app/edge/history/chart/widget/widget.component.ts +++ b/ui/src/app/edge/history/chart/widget/widget.component.ts @@ -21,7 +21,7 @@ export class WidgetComponent { ) { } ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route); this.service.getWidgets().then(widgets => { let result: string[] = []; for (let widget of widgets) { diff --git a/ui/src/app/edge/history/history.component.ts b/ui/src/app/edge/history/history.component.ts index c281be456f3..06e37aa0bfd 100644 --- a/ui/src/app/edge/history/history.component.ts +++ b/ui/src/app/edge/history/history.component.ts @@ -48,13 +48,12 @@ export class HistoryComponent implements OnInit { } ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => { + this.service.setCurrentComponent('', this.route).then(edge => { this.edge = edge; }); } updateOnWindowResize() { - //console.log(window.innerHeight, window.innerWidth); let ref = /* fix proportions */ Math.min(window.innerHeight - 150, /* handle grid breakpoints */(window.innerWidth < 768 ? window.innerWidth - 150 : window.innerWidth - 400)); this.socChartHeight = diff --git a/ui/src/app/edge/history/kwh/kwh.component.html b/ui/src/app/edge/history/kwh/kwh.component.html index c5b94e9ad32..348be4d7023 100644 --- a/ui/src/app/edge/history/kwh/kwh.component.html +++ b/ui/src/app/edge/history/kwh/kwh.component.html @@ -16,7 +16,7 @@
    - General.ChargePower + General.Production {{data["_sum/ProductionActiveEnergy"] / 1000 | number:'1.0-2'}} kWh @@ -25,6 +25,34 @@
    + + + + General.StorageSystem + + + + + + + + + + + + + +
    + General.ChargePower + + {{data["_sum/EssActiveChargeEnergy"] / 1000 | number:'1.0-2'}} kWh +
    + General.DischargePower + + {{data["_sum/EssActiveDischargeEnergy"] / 1000 | number:'1.0-2'}} kWh +
    +
    + @@ -72,4 +100,11 @@ + + + + General.ReportValue + +
    \ No newline at end of file diff --git a/ui/src/app/edge/history/kwh/kwh.component.ts b/ui/src/app/edge/history/kwh/kwh.component.ts index 34ae3f4c3d4..2a0bf126165 100644 --- a/ui/src/app/edge/history/kwh/kwh.component.ts +++ b/ui/src/app/edge/history/kwh/kwh.component.ts @@ -26,7 +26,9 @@ export class KwhComponent implements OnInit, OnChanges { ) { } ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route).then(response => { + this.edge = response; + }); } @@ -52,7 +54,9 @@ export class KwhComponent implements OnInit, OnChanges { new ChannelAddress('_sum', 'GridBuyActiveEnergy'), new ChannelAddress('_sum', 'GridSellActiveEnergy'), new ChannelAddress('_sum', 'ProductionActiveEnergy'), - new ChannelAddress('_sum', 'ConsumptionActiveEnergy') + new ChannelAddress('_sum', 'ConsumptionActiveEnergy'), + new ChannelAddress('_sum', 'EssActiveChargeEnergy'), + new ChannelAddress('_sum', 'EssActiveDischargeEnergy') ]); }); }; @@ -83,4 +87,32 @@ export class KwhComponent implements OnInit, OnChanges { }) } + /** + * Returns the given date's unix-milliseconds value + * + * @param date the date to format as unix-milliseconds + */ + private toUnix(date: Date): number { + return date.getTime(); + } + + /** + * Returns a new date, which represents the beginning of the given date's day + * + * @param date the date to process + */ + private startOfDay(date: Date): Date { + return new Date(date.getUTCFullYear(), date.getMonth(), date.getDate()); + } + + /** + * Returns a new date, which represents the end of the given date's day/the + * beginning of the following day + * + * @param date the date to process + */ + private endOfDay(date: Date): Date { + return new Date(date.getUTCFullYear(), date.getMonth(), date.getDate(), 24); + } + } diff --git a/ui/src/app/edge/index/energymonitor/chart/section/section.component.html b/ui/src/app/edge/index/energymonitor/chart/section/section.component.html deleted file mode 100644 index de23d05c4df..00000000000 --- a/ui/src/app/edge/index/energymonitor/chart/section/section.component.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - {{name}} - {{valueText}} - {{valueText2}} - - - - - - - - - - - \ No newline at end of file diff --git a/ui/src/app/edge/index/energytable/energytable.component.html b/ui/src/app/edge/index/energytable/energytable.component.html deleted file mode 100644 index d461c040ef6..00000000000 --- a/ui/src/app/edge/index/energytable/energytable.component.html +++ /dev/null @@ -1,162 +0,0 @@ - - - - - Edge.Index.Energytable.Title - - - - - - - - - - General.StorageSystem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    General.Soc{{ sum.soc | number:'1.0-0' }} %
    - General.ChargePower DC{{ sum.chargeActivePowerDC | number:'1.0-0' }} W
    General.ChargePower - AC - {{ sum.chargeActivePowerAC | number:'1.0-0' }} W-
    General.DischargePower - AC - {{ sum.dischargeActivePowerAC | number:'1.0-0' }} W-
    -
    - - - - - General.Grid - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    General.OffGrid
    General.GridBuy{{ sum.buyActivePower | number:'1.0-0' }} W-
    General.GridSell{{ sum.sellActivePower | number:'1.0-0' }} W-
    -
    - - - - - General.Production - - - - - - - - - - - - - - - - - - - - - - - -
    - General.Production DC{{ sum.activePowerDC | number:'1.0-0' }} W
    General.Production - AC - {{ sum.activePowerAC | number:'1.0-0' }} W-
    -
    - - - - - General.Consumption - - - - - - - - - - - - - - -
    {{ sum.activePower | number:'1.0-0' }} W-
    -
    - -
    -
    \ No newline at end of file diff --git a/ui/src/app/edge/index/energytable/energytable.component.ts b/ui/src/app/edge/index/energytable/energytable.component.ts deleted file mode 100644 index 6b16da7a7eb..00000000000 --- a/ui/src/app/edge/index/energytable/energytable.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, OnDestroy } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Service } from '../../../shared/service/service'; -import { Edge } from '../../../shared/edge/edge'; -import { Websocket } from '../../../shared/service/websocket'; -import { ChannelAddress } from '../../../shared/type/channeladdress'; - -@Component({ - selector: EnergytableComponent.SELECTOR, - templateUrl: './energytable.component.html' -}) -export class EnergytableComponent implements OnDestroy { - - private static readonly SELECTOR = "energytable"; - - public edge: Edge = null; - - constructor( - private service: Service, - private websocket: Websocket, - private route: ActivatedRoute - ) { } - - ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => { - this.edge = edge; - edge.subscribeChannels(this.websocket, EnergytableComponent.SELECTOR, [ - // Ess - new ChannelAddress('_sum', 'EssSoc'), new ChannelAddress('_sum', 'EssActivePower'), - // Grid - new ChannelAddress('_sum', 'GridActivePower'), - // Production - new ChannelAddress('_sum', 'ProductionActivePower'), new ChannelAddress('_sum', 'ProductionDcActualPower'), new ChannelAddress('_sum', 'ProductionAcActivePower'), new ChannelAddress('_sum', 'ProductionMaxActivePower'), - // Consumption - new ChannelAddress('_sum', 'ConsumptionActivePower'), new ChannelAddress('_sum', 'ConsumptionMaxActivePower') - ]); - }); - } - - ngOnDestroy() { - if (this.edge != null) { - this.edge.unsubscribeChannels(this.websocket, EnergytableComponent.SELECTOR); - } - } - -} diff --git a/ui/src/app/edge/index/history/history.component.html b/ui/src/app/edge/index/history/history.component.html deleted file mode 100644 index afe3c7a8096..00000000000 --- a/ui/src/app/edge/index/history/history.component.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - General.History - - - -
    - -
    - - - -
    -
    \ No newline at end of file diff --git a/ui/src/app/edge/index/history/history.component.ts b/ui/src/app/edge/index/history/history.component.ts deleted file mode 100644 index 01e495bf047..00000000000 --- a/ui/src/app/edge/index/history/history.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Component } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Edge, Service } from '../../../shared/shared'; - -@Component({ - selector: 'history', - templateUrl: './history.component.html' -}) -export class HistoryComponent { - - // show the chart for today - public fromDate = new Date(); - public toDate = new Date(); - - public edge: Edge; - - constructor( - private service: Service, - private route: ActivatedRoute - ) { } - - ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => this.edge = edge); - } -} diff --git a/ui/src/app/edge/index/index.module.ts b/ui/src/app/edge/index/index.module.ts deleted file mode 100644 index 0193e9addca..00000000000 --- a/ui/src/app/edge/index/index.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from '@angular/core'; -import { SharedModule } from './../../shared/shared.module'; -import { EnergymonitorModule } from './energymonitor/energymonitor.module'; -import { EnergytableComponent } from './energytable/energytable.component'; -import { HistoryComponent } from './history/history.component'; -import { IndexComponent } from './index.component'; -import { WidgetModule } from './widget/widget.module'; - -@NgModule({ - imports: [ - SharedModule, - EnergymonitorModule, - WidgetModule - ], - declarations: [ - IndexComponent, - EnergytableComponent, - HistoryComponent, - ] -}) -export class IndexModule { } diff --git a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.html b/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.html deleted file mode 100644 index 72f69c841cd..00000000000 --- a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.html +++ /dev/null @@ -1 +0,0 @@ -Hier werden demnächst die Eistellungen für die Ladesäule stehen \ No newline at end of file diff --git a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.spec.ts b/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.spec.ts deleted file mode 100644 index 72ffd61bf63..00000000000 --- a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { EvcsModalPage } from './evcs-modal.page'; - -describe('EvcsModalPage', () => { - let component: EvcsModalPage; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ EvcsModalPage ], - schemas: [CUSTOM_ELEMENTS_SCHEMA], - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(EvcsModalPage); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.ts b/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.ts deleted file mode 100644 index f4c5507ef92..00000000000 --- a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.page.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { environment } from 'src/environments/openems-backend-dev-local'; -import { PopoverController, ModalController } from '@ionic/angular'; -import { Router } from '@angular/router'; -import { Websocket } from 'src/app/shared/shared'; - -@Component({ - selector: 'app-evcs-modal', - templateUrl: './evcs-modal.page.html', - styleUrls: ['./evcs-modal.page.scss'], -}) -export class EvcsModalPage implements OnInit { - - public env = environment; - - constructor( - public popoverController: PopoverController, - public websocket: Websocket, - public router: Router - ) { } - - ngOnInit() { - } - - - -} diff --git a/ui/src/app/edge/index/widget/evcs/evcs.component.html b/ui/src/app/edge/index/widget/evcs/evcs.component.html deleted file mode 100644 index 26d92336330..00000000000 --- a/ui/src/app/edge/index/widget/evcs/evcs.component.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - Edge.Index.Widgets.EVCS.ChargingStation - - - - - - - - - - - - - - - -
    Edge.Index.Widgets.EVCS.ChargingPower - {{ outputPowerOrState(currentData[componentId + "/ChargePower"] | number:'1.0-0', - currentData[componentId + "/Status"], - currentData[componentId + "/Plug"]) }} -
    Edge.Index.Widgets.EVCS.EnergieSinceBeginning - {{ (currentData[componentId + "/EnergySession"] * 0.1) | number:'1.0-0' }} Wh -
    -
    - - - - Edge.Index.Widgets.EVCS.ChargeMode - - - - - - - Edge.Index.Widgets.EVCS.OptimizedChargeMode.Name - - Edge.Index.Widgets.EVCS.OptimizedChargeMode.ShortName - - - - - - - Edge.Index.Widgets.EVCS.ForceChargeMode.Name - - Edge.Index.Widgets.EVCS.ForceChargeMode.ShortName - - - - - - - - - - - -
    - Edge.Index.Widgets.EVCS.OptimizedChargeMode.Info
    - Edge.Index.Widgets.EVCS.OptimizedChargeMode.MinInfo - - - - - -
    Edge.Index.Widgets.EVCS.OptimizedChargeMode.MinCharging - - -
    - - - {{ 1400 * (getValueOrThree(currentData[componentId + '/Phases'])) | number:'1.0-0'}}  W - - - {{ formatNumber((currentData[componentId + '/HardwarePowerLimit']) * (getValueOrThree(currentData[componentId + '/Phases']))) | number:'1.0-0'}} W - - -
    - Edge.Index.Widgets.EVCS.ForceChargeMode.Info
    - Edge.Index.Widgets.EVCS.ForceChargeMode.MaxCharging - - - - {{ 1400 * (getValueOrThree(currentData[componentId + '/Phases'])) | number:'1.0-0'}} W - - - {{ formatNumber((currentData[componentId + '/HardwarePowerLimit']) * (getValueOrThree(currentData[componentId + '/Phases']))) | number:'1.0-0'}} W - - - - Edge.Index.Widgets.EVCS.ForceChargeMode.MaxChargingDetails -
    -
    -
    -
    - - - Edge.Index.Widgets.EVCS.NoConnection.Description -
      -
    • Edge.Index.Widgets.EVCS.NoConnection.Help1
    • -
        -
      • Edge.Index.Widgets.EVCS.NoConnection.Help1_1
      • -
      -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/ui/src/app/edge/index/widget/widget.module.ts b/ui/src/app/edge/index/widget/widget.module.ts deleted file mode 100644 index 4fbe1cda53b..00000000000 --- a/ui/src/app/edge/index/widget/widget.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgModule } from '@angular/core'; -import { SharedModule } from './../../../shared/shared.module'; -import { EvcsComponent } from './evcs/evcs.component'; -import { ModbusApiComponent } from './modbusapi/modbusapi.component'; -import { WidgetComponent } from './widget.component'; -import { ChannelthresholdComponent } from './channelthreshold/channelthreshold.component'; - -@NgModule({ - imports: [ - SharedModule - ], - declarations: [ - WidgetComponent, - ChannelthresholdComponent, - EvcsComponent, - ModbusApiComponent - ], - exports: [ - WidgetComponent - ] -}) -export class WidgetModule { } - - - diff --git a/ui/src/app/edge/index/energymonitor/chart/chart.component.html b/ui/src/app/edge/live/energymonitor/chart/chart.component.html similarity index 100% rename from ui/src/app/edge/index/energymonitor/chart/chart.component.html rename to ui/src/app/edge/live/energymonitor/chart/chart.component.html diff --git a/ui/src/app/edge/index/energymonitor/chart/chart.component.ts b/ui/src/app/edge/live/energymonitor/chart/chart.component.ts similarity index 100% rename from ui/src/app/edge/index/energymonitor/chart/chart.component.ts rename to ui/src/app/edge/live/energymonitor/chart/chart.component.ts diff --git a/ui/src/app/edge/index/energymonitor/chart/section/abstractsection.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/abstractsection.component.ts similarity index 100% rename from ui/src/app/edge/index/energymonitor/chart/section/abstractsection.component.ts rename to ui/src/app/edge/live/energymonitor/chart/section/abstractsection.component.ts diff --git a/ui/src/app/edge/index/energymonitor/chart/section/consumptionsection.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/consumptionsection.component.ts similarity index 100% rename from ui/src/app/edge/index/energymonitor/chart/section/consumptionsection.component.ts rename to ui/src/app/edge/live/energymonitor/chart/section/consumptionsection.component.ts diff --git a/ui/src/app/edge/index/energymonitor/chart/section/gridsection.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/gridsection.component.ts similarity index 98% rename from ui/src/app/edge/index/energymonitor/chart/section/gridsection.component.ts rename to ui/src/app/edge/live/energymonitor/chart/section/gridsection.component.ts index 160f9ccf5a5..38b748c3d9d 100644 --- a/ui/src/app/edge/index/energymonitor/chart/section/gridsection.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/section/gridsection.component.ts @@ -42,7 +42,7 @@ export class GridSectionComponent extends AbstractSection { Utils.divideSafely(sum.grid.sellActivePower, sum.system.totalPower)); } else { this.name = this.translate.instant('General.Grid') - super.updateSectionData(0, 0, 0); + super.updateSectionData(null, null, null); } // set grid mode diff --git a/ui/src/app/edge/index/energymonitor/chart/section/productionsection.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/productionsection.component.ts similarity index 100% rename from ui/src/app/edge/index/energymonitor/chart/section/productionsection.component.ts rename to ui/src/app/edge/live/energymonitor/chart/section/productionsection.component.ts diff --git a/ui/src/app/edge/live/energymonitor/chart/section/section.component.html b/ui/src/app/edge/live/energymonitor/chart/section/section.component.html new file mode 100644 index 00000000000..6d8bdcfb768 --- /dev/null +++ b/ui/src/app/edge/live/energymonitor/chart/section/section.component.html @@ -0,0 +1,27 @@ + + + + {{name}} + + {{valueText}} + - + + {{valueText2}} + + + + + + + + + + + \ No newline at end of file diff --git a/ui/src/app/edge/index/energymonitor/chart/section/storagesection.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/storagesection.component.ts similarity index 88% rename from ui/src/app/edge/index/energymonitor/chart/section/storagesection.component.ts rename to ui/src/app/edge/live/energymonitor/chart/section/storagesection.component.ts index 574e9d09b58..637e8f715fe 100644 --- a/ui/src/app/edge/index/energymonitor/chart/section/storagesection.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/section/storagesection.component.ts @@ -36,22 +36,23 @@ export class StorageSectionComponent extends AbstractSection implements OnInit { } public _updateCurrentData(sum: DefaultTypes.Summary): void { - let power = Utils.subtractSafely(sum.storage.chargeActivePowerAC, sum.storage.dischargeActivePowerAC); - if (power == null || power == 0) { - this.name = this.translate.instant('Edge.Index.Energymonitor.Storage') - super.updateSectionData(0, 0, 0); - } else if (power > 0) { + if (sum.storage.effectiveChargePower != null) { this.name = this.translate.instant('Edge.Index.Energymonitor.StorageCharge'); super.updateSectionData( - power, + sum.storage.effectiveChargePower, sum.storage.powerRatio, - Utils.divideSafely(power, sum.system.totalPower)); - } else { + Utils.divideSafely(sum.storage.effectiveChargePower, sum.system.totalPower)); + + } else if (sum.storage.effectiveDischargePower != null) { this.name = this.translate.instant('Edge.Index.Energymonitor.StorageDischarge'); super.updateSectionData( - power * -1, + sum.storage.effectiveDischargePower, sum.storage.powerRatio, - Utils.divideSafely(power, sum.system.totalPower)); + Utils.divideSafely(sum.storage.effectiveDischargePower, sum.system.totalPower)); + + } else { + this.name = this.translate.instant('Edge.Index.Energymonitor.Storage') + super.updateSectionData(null, null, null); } this.socValue = sum.storage.soc; diff --git a/ui/src/app/edge/index/energymonitor/energymonitor.component.html b/ui/src/app/edge/live/energymonitor/energymonitor.component.html similarity index 65% rename from ui/src/app/edge/index/energymonitor/energymonitor.component.html rename to ui/src/app/edge/live/energymonitor/energymonitor.component.html index f4d55e16deb..45607397581 100644 --- a/ui/src/app/edge/index/energymonitor/energymonitor.component.html +++ b/ui/src/app/edge/live/energymonitor/energymonitor.component.html @@ -1,10 +1,10 @@ - - - - Edge.Index.Energymonitor.Title + + + + Edge.Index.Energymonitor.Title diff --git a/ui/src/app/edge/index/energymonitor/energymonitor.component.ts b/ui/src/app/edge/live/energymonitor/energymonitor.component.ts similarity index 95% rename from ui/src/app/edge/index/energymonitor/energymonitor.component.ts rename to ui/src/app/edge/live/energymonitor/energymonitor.component.ts index d81fa7dab9f..5a1f8465805 100644 --- a/ui/src/app/edge/index/energymonitor/energymonitor.component.ts +++ b/ui/src/app/edge/live/energymonitor/energymonitor.component.ts @@ -19,7 +19,7 @@ export class EnergymonitorComponent { ) { } ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => { + this.service.setCurrentComponent('', this.route).then(edge => { this.edge = edge; edge.subscribeChannels(this.websocket, EnergymonitorComponent.SELECTOR, [ // Ess diff --git a/ui/src/app/edge/index/energymonitor/energymonitor.module.ts b/ui/src/app/edge/live/energymonitor/energymonitor.module.ts similarity index 100% rename from ui/src/app/edge/index/energymonitor/energymonitor.module.ts rename to ui/src/app/edge/live/energymonitor/energymonitor.module.ts diff --git a/ui/src/app/edge/index/index.component.html b/ui/src/app/edge/live/live.component.html similarity index 52% rename from ui/src/app/edge/index/index.component.html rename to ui/src/app/edge/live/live.component.html index b217e09193f..bd113e7a4c8 100644 --- a/ui/src/app/edge/index/index.component.html +++ b/ui/src/app/edge/live/live.component.html @@ -1,21 +1,13 @@ - + - - + - - - - - - - - + \ No newline at end of file diff --git a/ui/src/app/edge/index/index.component.ts b/ui/src/app/edge/live/live.component.ts similarity index 68% rename from ui/src/app/edge/index/index.component.ts rename to ui/src/app/edge/live/live.component.ts index 26555e91244..ace5d64d41c 100644 --- a/ui/src/app/edge/index/index.component.ts +++ b/ui/src/app/edge/live/live.component.ts @@ -3,10 +3,10 @@ import { ActivatedRoute } from '@angular/router'; import { Edge, Service, Utils } from '../../shared/shared'; @Component({ - selector: 'index', - templateUrl: './index.component.html' + selector: 'live', + templateUrl: './live.component.html' }) -export class IndexComponent implements OnInit { +export class LiveComponent implements OnInit { public edge: Edge = null @@ -18,7 +18,7 @@ export class IndexComponent implements OnInit { } ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => { + this.service.setCurrentComponent('', this.route).then(edge => { this.edge = edge }); } diff --git a/ui/src/app/edge/live/live.module.ts b/ui/src/app/edge/live/live.module.ts new file mode 100644 index 00000000000..c5eaa1af37b --- /dev/null +++ b/ui/src/app/edge/live/live.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from './../../shared/shared.module'; +import { EnergymonitorModule } from './energymonitor/energymonitor.module'; +import { WidgetsModule } from './widgets/widgets.module'; +import { LiveComponent } from './live.component'; + +@NgModule({ + imports: [ + SharedModule, + EnergymonitorModule, + WidgetsModule + ], + declarations: [ + LiveComponent, + ] +}) +export class LiveModule { } diff --git a/ui/src/app/edge/index/widget/channelthreshold/channelthreshold.component.html b/ui/src/app/edge/live/widgets/channelthreshold/channelthreshold.component.html similarity index 100% rename from ui/src/app/edge/index/widget/channelthreshold/channelthreshold.component.html rename to ui/src/app/edge/live/widgets/channelthreshold/channelthreshold.component.html diff --git a/ui/src/app/edge/index/widget/channelthreshold/channelthreshold.component.ts b/ui/src/app/edge/live/widgets/channelthreshold/channelthreshold.component.ts similarity index 94% rename from ui/src/app/edge/index/widget/channelthreshold/channelthreshold.component.ts rename to ui/src/app/edge/live/widgets/channelthreshold/channelthreshold.component.ts index d85222b9f76..eb4f765035d 100644 --- a/ui/src/app/edge/index/widget/channelthreshold/channelthreshold.component.ts +++ b/ui/src/app/edge/live/widgets/channelthreshold/channelthreshold.component.ts @@ -24,7 +24,7 @@ export class ChannelthresholdComponent { ngOnInit() { // Subscribe to CurrentData - this.service.setCurrentEdge(this.route).then(edge => { + this.service.setCurrentComponent('', this.route).then(edge => { this.edge = edge; this.service.getConfig().then(config => { this.outputChannel = ChannelAddress.fromString(config.getComponentProperties(this.componentId)['outputChannelAddress']); diff --git a/ui/src/app/edge/live/widgets/consumption/consumption.component.html b/ui/src/app/edge/live/widgets/consumption/consumption.component.html new file mode 100644 index 00000000000..555be2ec378 --- /dev/null +++ b/ui/src/app/edge/live/widgets/consumption/consumption.component.html @@ -0,0 +1,29 @@ + + + + + + + + General.Consumption + + + + + + + + + + + + + + + +
    {{ sum.activePower | number:'1.0-0' }} W-
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/consumption/consumption.component.ts b/ui/src/app/edge/live/widgets/consumption/consumption.component.ts new file mode 100644 index 00000000000..7ec92b6163b --- /dev/null +++ b/ui/src/app/edge/live/widgets/consumption/consumption.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ChannelAddress, Edge, Service, Websocket } from '../../../../shared/shared'; + +@Component({ + selector: 'consumption', + templateUrl: './consumption.component.html' +}) +export class ConsumptionComponent { + + private static readonly SELECTOR = "consumption"; + + public edge: Edge = null; + + constructor( + public service: Service, + private websocket: Websocket, + private route: ActivatedRoute, + ) { } + + ngOnInit() { + this.service.setCurrentComponent('', this.route).then(edge => { + this.edge = edge; + edge.subscribeChannels(this.websocket, ConsumptionComponent.SELECTOR, [ + // Consumption + new ChannelAddress('_sum', 'ConsumptionActivePower'), + new ChannelAddress('_sum', 'ConsumptionMaxActivePower') + ]); + }); + } + + ngOnDestroy() { + if (this.edge != null) { + this.edge.unsubscribeChannels(this.websocket, ConsumptionComponent.SELECTOR); + } + } +} diff --git a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.module.ts b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.module.ts similarity index 62% rename from ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.module.ts rename to ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.module.ts index 37d64153b1c..58d6d4cdd39 100644 --- a/ui/src/app/edge/index/widget/evcs/evcs-modal/evcs-modal.module.ts +++ b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.module.ts @@ -6,6 +6,8 @@ import { Routes, RouterModule } from '@angular/router'; import { IonicModule } from '@ionic/angular'; import { EvcsModalPage } from './evcs-modal.page'; +import { SharedModule } from 'src/app/shared/shared.module'; +import { InfoPopoverComponent } from './info-popover/info-popover.component'; const routes: Routes = [ { @@ -19,8 +21,12 @@ const routes: Routes = [ CommonModule, FormsModule, IonicModule, + SharedModule, RouterModule.forChild(routes) ], - declarations: [EvcsModalPage] + entryComponents: [InfoPopoverComponent], + declarations: [EvcsModalPage, InfoPopoverComponent] }) -export class EvcsModalPageModule {} +export class EvcsModalPageModule { + +} \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.html b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.html new file mode 100644 index 00000000000..7dc92abb712 --- /dev/null +++ b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.html @@ -0,0 +1,221 @@ + + + Ladestation + + + + + + + + + + + + + + + + + + + + + + + + + +
    Edge.Index.Widgets.EVCS.ActivateCharging + + +
    Edge.Index.Widgets.EVCS.ChargingPower + {{ outputPowerOrState(currentData[componentId + "/ChargePower"] | number:'1.0-0', + currentData[componentId + "/Status"], + currentData[componentId + "/Plug"]) }} +
    Edge.Index.Widgets.EVCS.EnergieSinceBeginning + {{ (currentData[componentId + "/EnergySession"] * 0.1) | number:'1.0-0' }} Wh +
    +
    +
    + + + + + + + + + +
    + + + + + + + + + Edge.Index.Widgets.EVCS.OptimizedChargeMode.ShortName + + + + + + + + + + + Edge.Index.Widgets.EVCS.ForceChargeMode.ShortName + + + + + + + + + + +
    + + + + + + +
    + + Edge.Index.Widgets.EVCS.OptimizedChargeMode.Info + + + + Edge.Index.Widgets.EVCS.ForceChargeMode.Info + +
    + + + + + + + + + +
    + + + + + +
    + Edge.Index.Widgets.EVCS.OptimizedChargeMode.MinCharging + + +
    + + + {{ formatNumber(currentData[componentId + '/MinimumPower']) | number:'1.0-0'}} +  W + + + {{ formatNumber(currentData[componentId + '/MaximumPower']) | number:'1.0-0'}} W + + + + + + + + +
    Priorisierung: + + + + Auto + + + Speicher + + +
    +
    + + Edge.Index.Widgets.EVCS.OptimizedChargeMode.ChargingPriority.Info + +
    + Edge.Index.Widgets.EVCS.ForceChargeMode.MaxCharging + + + + + {{ formatNumber(currentData[componentId + '/MinimumPower']) | number:'1.0-0'}} W + + + {{ formatNumber(currentData[componentId + '/MaximumPower']) | number:'1.0-0'}} W + + + + Edge.Index.Widgets.EVCS.ForceChargeMode.MaxChargingDetails + +
    +
    +
    +
    +
    + + + Edge.Index.Widgets.EVCS.NoConnection.Description +
      +
    • Edge.Index.Widgets.EVCS.NoConnection.Help1
    • +
        +
      • Edge.Index.Widgets.EVCS.NoConnection.Help1_1
      • +
      +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.scss b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.scss new file mode 100644 index 00000000000..f606786cfe3 --- /dev/null +++ b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.scss @@ -0,0 +1,13 @@ +ion-card { + --margin-left: 0; + --margin-right: 0; +} + +.full-card{ + margin-left: 0px; + margin-right: 0px +} + +.icon{ + font-size: 20px; +} \ No newline at end of file diff --git a/ui/src/app/edge/index/widget/evcs/evcs.component.ts b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.ts similarity index 67% rename from ui/src/app/edge/index/widget/evcs/evcs.component.ts rename to ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.ts index ee567cded1e..d254bca77e2 100644 --- a/ui/src/app/edge/index/widget/evcs/evcs.component.ts +++ b/ui/src/app/edge/live/widgets/evcs/evcs-modal/evcs-modal.page.ts @@ -1,85 +1,44 @@ -import { Component, Input, HostListener } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { ChannelAddress, Edge, EdgeConfig, Service, Websocket } from '../../../../shared/shared'; +import { Component, OnInit, HostListener, Input } from '@angular/core'; +import { environment } from 'src/environments/openems-backend-dev-local'; +import { PopoverController, ModalController } from '@ionic/angular'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Websocket, ChannelAddress, Service, EdgeConfig, Edge } from 'src/app/shared/shared'; import { TranslateService } from '@ngx-translate/core'; -import { ModalController } from '@ionic/angular'; -import { EvcsModalPage } from './evcs-modal/evcs-modal.page'; - +import { EvcsComponent } from '../evcs.component'; +import { InfoPopoverComponent } from './info-popover/info-popover.component'; type ChargeMode = 'FORCE_CHARGE' | 'EXCESS_POWER'; +type Priority = 'CAR' | 'STORAGE'; @Component({ - selector: 'evcs', - templateUrl: './evcs.component.html' + selector: 'app-evcs-modal', + templateUrl: './evcs-modal.page.html', + styleUrls: ['./evcs-modal.page.scss'], }) -export class EvcsComponent { - - private static readonly SELECTOR = "evcs"; - - - @Input() private componentId: string; +export class EvcsModalPage implements OnInit { - public edge: Edge = null; - public controller: EdgeConfig.Component = null; + @Input() controller: EdgeConfig.Component; + @Input() edge: Edge; + @Input() componentId: number; public chargeState: ChargeState; private chargePlug: ChargePlug; public screenWidth: number = 0; + public env = environment; constructor( - private service: Service, - private websocket: Websocket, - private route: ActivatedRoute, + public websocket: Websocket, + public router: Router, protected translate: TranslateService, - public modalController: ModalController + private modalCtrl: ModalController, + private popoverController: PopoverController ) { } ngOnInit() { - this.getScreenSize(); - - // Subscribe to CurrentData - this.service.setCurrentEdge(this.route).then(edge => { - this.edge = edge; - edge.subscribeChannels(this.websocket, EvcsComponent.SELECTOR + this.componentId, [ - // Evcs - new ChannelAddress(this.componentId, 'ChargePower'), - new ChannelAddress(this.componentId, 'HardwarePowerLimit'), - new ChannelAddress(this.componentId, 'Phases'), - new ChannelAddress(this.componentId, 'Plug'), - new ChannelAddress(this.componentId, 'Status'), - new ChannelAddress(this.componentId, 'State'), - new ChannelAddress(this.componentId, 'EnergySession') - ]); - - }); - - // Gets the Controller for the given EVCS-Component. - this.service.getConfig().then(config => { - let controllers = config.getComponentsByFactory("Controller.Evcs"); - for (let controller of controllers) { - let properties = controller.properties; - if ("evcs.id" in properties && properties["evcs.id"] === this.componentId) { - // this 'controller' is the Controller responsible for this EVCS - this.controller = controller; - return; - } - } - }); - } - ngOnDestroy() { - if (this.edge != null) { - this.edge.unsubscribeChannels(this.websocket, EvcsComponent.SELECTOR + this.componentId); - } - } - - async presentModal() { - const modal = await this.modalController.create({ - component: EvcsModalPage, - componentProps: { value: 123 } - }); - return await modal.present(); + cancel() { + this.modalCtrl.dismiss(); } /** @@ -104,14 +63,39 @@ export class EvcsComponent { this.edge.updateComponentConfig(this.websocket, this.controller.id, [ { name: 'chargeMode', value: newChargeMode } ]).then(response => { - console.log("HIER", response); this.controller.properties.chargeMode = newChargeMode; }).catch(reason => { this.controller.properties.chargeMode = oldChargeMode; console.warn(reason); }); } + } + /** + * Changed the Priority between the components of the charging session + */ + priorityChanged(event: CustomEvent) { + let oldPriority = this.controller.properties.priority; + let newPriority: Priority; + switch (event.detail.value) { + case 'CAR': + newPriority = 'CAR'; + break; + case 'STORAGE': + newPriority = 'STORAGE'; + break; + } + + if (this.edge != null) { + this.edge.updateComponentConfig(this.websocket, this.controller.id, [ + { name: 'priority', value: newPriority } + ]).then(response => { + this.controller.properties.priority = newPriority; + }).catch(reason => { + this.controller.properties.priority = oldPriority; + console.warn(reason); + }); + } } /** @@ -127,7 +111,6 @@ export class EvcsComponent { this.edge.updateComponentConfig(this.websocket, this.controller.id, [ { name: 'forceChargeMinPower', value: newMinChargePower } ]).then(response => { - console.log("HIER", response); this.controller.properties.forceChargeMinPower = newMinChargePower; }).catch(reason => { this.controller.properties.forceChargeMinPower = oldMinChargePower; @@ -149,7 +132,6 @@ export class EvcsComponent { this.edge.updateComponentConfig(this.websocket, this.controller.id, [ { name: 'defaultChargeMinPower', value: newMinChargePower } ]).then(response => { - console.log("HIER", response); this.controller.properties.defaultChargeMinPower = newMinChargePower; }).catch(reason => { this.controller.properties.defaultChargeMinPower = oldMinChargePower; @@ -176,7 +158,6 @@ export class EvcsComponent { this.edge.updateComponentConfig(this.websocket, this.controller.id, [ { name: 'defaultChargeMinPower', value: newMinChargePower } ]).then(response => { - console.log("HIER", response); this.controller.properties.defaultChargeMinPower = newMinChargePower; }).catch(reason => { this.controller.properties.defaultChargeMinPower = oldMinChargePower; @@ -185,6 +166,26 @@ export class EvcsComponent { } } + /** + * Aktivates or deaktivates the Charging + * + * @param event + */ + enableOrDisableCharging(event: CustomEvent) { + + let oldChargingState = this.controller.properties.enabledCharging; + let newChargingState = !oldChargingState; + if (this.edge != null) { + this.edge.updateComponentConfig(this.websocket, this.controller.id, [ + { name: 'enabledCharging', value: newChargingState } + ]).then(response => { + this.controller.properties.enabledCharging = newChargingState; + }).catch(reason => { + this.controller.properties.enabledCharging = oldChargingState; + console.warn(reason); + }); + } + } /** * Gets the output for the current state or the current charging power * @@ -199,8 +200,9 @@ export class EvcsComponent { this.chargeState = state; this.chargePlug = plug; - if (this.chargePlug != ChargePlug.PLUGGED_ON_EVCS_AND_ON_EV_AND_LOCKED) + if (this.chargePlug != ChargePlug.PLUGGED_ON_EVCS_AND_ON_EV_AND_LOCKED) { return this.translate.instant('Edge.Index.Widgets.EVCS.CableNotConnected'); + } switch (this.chargeState) { case ChargeState.STARTING: @@ -220,13 +222,30 @@ export class EvcsComponent { } + async presentPopover(ev: any, mode: ChargeMode) { + const popover = await this.popoverController.create({ + component: InfoPopoverComponent, + event: ev, + componentProps: { + chargeMode: mode + } + }); + return await popover.present(); + } + + dismissPopover(ev: any) { + this.popoverController.dismiss(); + } + /** - * Round to 100 + * Round to 100 and + * Round up (ceil) * * @param i */ formatNumber(i: number) { - return Math.round(i / 100) * 100; + let round = Math.ceil(i / 100) * 100; + return round; } /** @@ -264,5 +283,4 @@ enum ChargePlug { PLUGGED_ON_EVCS_AND_LOCKED = 3, //Plugged on EVCS and locked PLUGGED_ON_EVCS_AND_ON_EV = 5, //Plugged on EVCS and on EV PLUGGED_ON_EVCS_AND_ON_EV_AND_LOCKED = 7 //Plugged on EVCS and on EV and locked - -} \ No newline at end of file +} diff --git a/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.html b/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.html new file mode 100644 index 00000000000..ac6d9257ecf --- /dev/null +++ b/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.html @@ -0,0 +1,22 @@ + + + Info + + + + + + + + + + Edge.Index.Widgets.EVCS.OptimizedChargeMode.Info + +
    + + Edge.Index.Widgets.EVCS.OptimizedChargeMode.MinInfo + +
    + + Edge.Index.Widgets.EVCS.ForceChargeMode.Info + \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.scss b/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.ts b/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.ts new file mode 100644 index 00000000000..65c5ad8b9d4 --- /dev/null +++ b/ui/src/app/edge/live/widgets/evcs/evcs-modal/info-popover/info-popover.component.ts @@ -0,0 +1,21 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { PopoverController } from '@ionic/angular'; + +@Component({ + selector: 'app-info-popover', + templateUrl: './info-popover.component.html', + styleUrls: ['./info-popover.component.scss'], +}) +export class InfoPopoverComponent implements OnInit { + + @Input() chargeMode: String; + + constructor(public popoverController: PopoverController) { } + + ngOnInit() { + } + + cancel() { + this.popoverController.dismiss(); + } +} diff --git a/ui/src/app/edge/live/widgets/evcs/evcs.component.html b/ui/src/app/edge/live/widgets/evcs/evcs.component.html new file mode 100644 index 00000000000..76f1c5c43f4 --- /dev/null +++ b/ui/src/app/edge/live/widgets/evcs/evcs.component.html @@ -0,0 +1,47 @@ + + + + + + + + + + Edge.Index.Widgets.EVCS.ChargingStation + + + + + + + + + + + + + + + +
    Edge.Index.Widgets.EVCS.ChargingPower + {{ outputPowerOrState(currentData[componentId + "/ChargePower"] | number:'1.0-0', + currentData[componentId + "/Status"], + currentData[componentId + "/Plug"]) }} +
    Edge.Index.Widgets.EVCS.EnergieSinceBeginning + {{ (currentData[componentId + "/EnergySession"] * 0.1) | number:'1.0-0' }} Wh +
    +
    +
    + + + Edge.Index.Widgets.EVCS.NoConnection.Description +
      +
    • Edge.Index.Widgets.EVCS.NoConnection.Help1
    • +
        +
      • Edge.Index.Widgets.EVCS.NoConnection.Help1_1
      • +
      +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/evcs/evcs.component.ts b/ui/src/app/edge/live/widgets/evcs/evcs.component.ts new file mode 100644 index 00000000000..a0b380834ae --- /dev/null +++ b/ui/src/app/edge/live/widgets/evcs/evcs.component.ts @@ -0,0 +1,192 @@ +import { Component, Input, HostListener } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ChannelAddress, Edge, EdgeConfig, Service, Websocket } from '../../../../shared/shared'; +import { TranslateService } from '@ngx-translate/core'; +import { ModalController } from '@ionic/angular'; +import { EvcsModalPage } from './evcs-modal/evcs-modal.page'; +import { componentFactoryName } from '@angular/compiler'; + +type ChargeMode = 'FORCE_CHARGE' | 'EXCESS_POWER'; +type Priority = 'CAR' | 'STORAGE'; + +@Component({ + selector: 'evcs', + templateUrl: './evcs.component.html' +}) +export class EvcsComponent { + + private static readonly SELECTOR = "evcs"; + + + @Input() private componentId: string; + + public edge: Edge = null; + public controller: EdgeConfig.Component = null; + public chargeState: ChargeState; + private chargePlug: ChargePlug; + public screenWidth: number = 0; + + constructor( + private service: Service, + private websocket: Websocket, + private route: ActivatedRoute, + protected translate: TranslateService, + public modalController: ModalController + ) { } + + ngOnInit() { + + // Subscribe to CurrentData + this.service.setCurrentComponent('', this.route).then(edge => { + this.edge = edge; + edge.subscribeChannels(this.websocket, EvcsComponent.SELECTOR + this.componentId, [ + // Evcs + new ChannelAddress(this.componentId, 'ChargePower'), + new ChannelAddress(this.componentId, 'HardwarePowerLimit'), + new ChannelAddress(this.componentId, 'Phases'), + new ChannelAddress(this.componentId, 'Plug'), + new ChannelAddress(this.componentId, 'Status'), + new ChannelAddress(this.componentId, 'State'), + new ChannelAddress(this.componentId, 'EnergySession'), + new ChannelAddress(this.componentId, 'MinimumPower'), + new ChannelAddress(this.componentId, 'MaximumPower') + ]); + + }); + + // Gets the Controller for the given EVCS-Component. + this.service.getConfig().then(config => { + let controllers = config.getComponentsByFactory("Controller.Evcs"); + for (let controller of controllers) { + let properties = controller.properties; + if ("evcs.id" in properties && properties["evcs.id"] === this.componentId) { + // this 'controller' is the Controller responsible for this EVCS + this.controller = controller; + return; + } + } + }); + + } + + ngOnDestroy() { + if (this.edge != null) { + this.edge.unsubscribeChannels(this.websocket, EvcsComponent.SELECTOR + this.componentId); + } + } + + async presentModal() { + const modal = await this.modalController.create({ + component: EvcsModalPage, + componentProps: { + controller: this.controller, + edge: this.edge, + componentId: this.componentId + } + }); + return await modal.present(); + } + + /** + * Aktivates or deaktivates the Charging + * + * @param event + */ + protected enableOrDisableCharging(event: CustomEvent) { + + let oldChargingState = this.controller.properties.enabledCharging; + let newChargingState = !oldChargingState; + if (this.edge != null) { + this.edge.updateComponentConfig(this.websocket, this.controller.id, [ + { name: 'enabledCharging', value: newChargingState } + ]).then(response => { + this.controller.properties.enabledCharging = newChargingState; + }).catch(reason => { + this.controller.properties.enabledCharging = oldChargingState; + console.warn(reason); + }); + } + } + + /** + * Gets the output for the current state or the current charging power + * + * @param power + * @param state + * @param plug + */ + outputPowerOrState(power: Number, state: number, plug: number) { + + if (power == null || power == 0) { + + this.chargeState = state; + this.chargePlug = plug; + + if (this.chargePlug != ChargePlug.PLUGGED_ON_EVCS_AND_ON_EV_AND_LOCKED) + return this.translate.instant('Edge.Index.Widgets.EVCS.CableNotConnected'); + + switch (this.chargeState) { + case ChargeState.STARTING: + return this.translate.instant('Edge.Index.Widgets.EVCS.Starting'); + case ChargeState.UNDEFINED: + case ChargeState.ERROR: + return this.translate.instant('Edge.Index.Widgets.EVCS.Error'); + case ChargeState.READY_FOR_CHARGING: + return this.translate.instant('Edge.Index.Widgets.EVCS.CarFull'); + case ChargeState.NOT_READY_FOR_CHARGING: + return this.translate.instant('Edge.Index.Widgets.EVCS.NotReadyForCharging'); + case ChargeState.AUTHORIZATION_REJECTED: + return power + " W"; + } + } + return power + " W"; + + } + + /** + * Round to 100 and + * Round up (ceil) + * + * @param i + */ + formatNumber(i: number) { + let round = Math.ceil(i / 100) * 100; + return round; + } + + /** + * Get Value or 3 + * + * @param i + */ + getValueOrThree(i: number) { + if (i == null || i == undefined) { + return 3; + } else { + return i; + } + } + + @HostListener('window:resize', ['$event']) + getScreenSize(event?) { + this.screenWidth = window.innerWidth; + } +} +enum ChargeState { + UNDEFINED = -1, //Undefined + STARTING, //Starting + NOT_READY_FOR_CHARGING, //Not ready for Charging e.g. unplugged, X1 or "ena" not enabled, RFID not enabled,... + READY_FOR_CHARGING, //Ready for Charging waiting for EV charging request + CHARGING, //Charging + ERROR, //Error + AUTHORIZATION_REJECTED //Authorization rejected +} + +enum ChargePlug { + UNDEFINED = -1, //Undefined + UNPLUGGED, //Unplugged + PLUGGED_ON_EVCS, //Plugged on EVCS + PLUGGED_ON_EVCS_AND_LOCKED = 3, //Plugged on EVCS and locked + PLUGGED_ON_EVCS_AND_ON_EV = 5, //Plugged on EVCS and on EV + PLUGGED_ON_EVCS_AND_ON_EV_AND_LOCKED = 7 //Plugged on EVCS and on EV and locked +} diff --git a/ui/src/app/edge/live/widgets/grid/grid.component.html b/ui/src/app/edge/live/widgets/grid/grid.component.html new file mode 100644 index 00000000000..52b3493caed --- /dev/null +++ b/ui/src/app/edge/live/widgets/grid/grid.component.html @@ -0,0 +1,44 @@ + + + + + + + + General.Grid + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    General.OffGrid
    General.GridBuy{{ sum.buyActivePower | number:'1.0-0' }} W-
    General.GridSell{{ sum.sellActivePower | number:'1.0-0' }} +  W-
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/grid/grid.component.ts b/ui/src/app/edge/live/widgets/grid/grid.component.ts new file mode 100644 index 00000000000..290943c158b --- /dev/null +++ b/ui/src/app/edge/live/widgets/grid/grid.component.ts @@ -0,0 +1,36 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ChannelAddress, Edge, Service, Websocket } from '../../../../shared/shared'; + +@Component({ + selector: 'grid', + templateUrl: './grid.component.html' +}) +export class GridComponent { + + private static readonly SELECTOR = "grid"; + + public edge: Edge = null; + + constructor( + public service: Service, + private websocket: Websocket, + private route: ActivatedRoute, + ) { } + + ngOnInit() { + this.service.setCurrentComponent('', this.route).then(edge => { + this.edge = edge; + edge.subscribeChannels(this.websocket, GridComponent.SELECTOR, [ + // Grid + new ChannelAddress('_sum', 'GridActivePower'), + ]); + }); + } + + ngOnDestroy() { + if (this.edge != null) { + this.edge.unsubscribeChannels(this.websocket, GridComponent.SELECTOR); + } + } +} diff --git a/ui/src/app/edge/live/widgets/info/info.component.html b/ui/src/app/edge/live/widgets/info/info.component.html new file mode 100644 index 00000000000..80401826117 --- /dev/null +++ b/ui/src/app/edge/live/widgets/info/info.component.html @@ -0,0 +1,12 @@ + + + + Info + + + + Durch eine Umstellung des Monitorings finden Sie das Diagramm für den Ladezustand ab sofort in der + Darstellung der historischen Daten. Klicken Sie dazu oben auf "HISTORIE". + + + \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/info/info.component.ts b/ui/src/app/edge/live/widgets/info/info.component.ts new file mode 100644 index 00000000000..8ef8d9228ba --- /dev/null +++ b/ui/src/app/edge/live/widgets/info/info.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { Edge } from '../../../../shared/shared'; + +@Component({ + selector: 'info', + templateUrl: './info.component.html' +}) +export class InfoComponent { + + constructor( + ) { } + + ngOnInit() { } + + ngOnDestroy() { } +} diff --git a/ui/src/app/edge/live/widgets/modbusapi/getModbusProtocolExportXlsxRequest.ts b/ui/src/app/edge/live/widgets/modbusapi/getModbusProtocolExportXlsxRequest.ts new file mode 100644 index 00000000000..977ab8f5b4d --- /dev/null +++ b/ui/src/app/edge/live/widgets/modbusapi/getModbusProtocolExportXlsxRequest.ts @@ -0,0 +1,25 @@ +import { JsonrpcRequest } from '../../../../shared/jsonrpc/base'; + +/** + * Wraps a JSON-RPC Request to query the Modbus Protocol from Modbus/TCP + * Api-Controller + * + *
    + * {
    + *   "jsonrpc": "2.0",
    + *   "id": "UUID",
    + *   "method": "getModbusProtocolXlsx",
    + *   "params": {}
    + * }
    + * 
    + */ +export class GetModbusProtocolExportXlsxRequest extends JsonrpcRequest { + + static METHOD: string = "getModbusProtocolExportXlsx"; + + public constructor( + ) { + super(GetModbusProtocolExportXlsxRequest.METHOD, {}); + } + +} \ No newline at end of file diff --git a/ui/src/app/edge/index/widget/modbusapi/getModbusProtocolRequest.ts b/ui/src/app/edge/live/widgets/modbusapi/getModbusProtocolRequest.ts similarity index 100% rename from ui/src/app/edge/index/widget/modbusapi/getModbusProtocolRequest.ts rename to ui/src/app/edge/live/widgets/modbusapi/getModbusProtocolRequest.ts diff --git a/ui/src/app/edge/index/widget/modbusapi/getModbusProtocolResponse.ts b/ui/src/app/edge/live/widgets/modbusapi/getModbusProtocolResponse.ts similarity index 100% rename from ui/src/app/edge/index/widget/modbusapi/getModbusProtocolResponse.ts rename to ui/src/app/edge/live/widgets/modbusapi/getModbusProtocolResponse.ts diff --git a/ui/src/app/edge/index/widget/modbusapi/modbusapi.component.html b/ui/src/app/edge/live/widgets/modbusapi/modbusapi.component.html similarity index 61% rename from ui/src/app/edge/index/widget/modbusapi/modbusapi.component.html rename to ui/src/app/edge/live/widgets/modbusapi/modbusapi.component.html index 86084cc4111..fe56a35dfce 100644 --- a/ui/src/app/edge/index/widget/modbusapi/modbusapi.component.html +++ b/ui/src/app/edge/live/widgets/modbusapi/modbusapi.component.html @@ -1,10 +1,8 @@ - + - - Modbus/TCP Api - - + Modbus/TCP Api + diff --git a/ui/src/app/edge/index/widget/modbusapi/modbusapi.component.ts b/ui/src/app/edge/live/widgets/modbusapi/modbusapi.component.ts similarity index 61% rename from ui/src/app/edge/index/widget/modbusapi/modbusapi.component.ts rename to ui/src/app/edge/live/widgets/modbusapi/modbusapi.component.ts index f6886c311be..227a8aea73e 100644 --- a/ui/src/app/edge/index/widget/modbusapi/modbusapi.component.ts +++ b/ui/src/app/edge/live/widgets/modbusapi/modbusapi.component.ts @@ -1,11 +1,10 @@ import { Component, Input } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import * as FileSaver from 'file-saver'; -import * as XLSX from 'xlsx'; import { ComponentJsonApiRequest } from '../../../../shared/jsonrpc/request/componentJsonApiRequest'; import { EdgeConfig, Service, Websocket } from '../../../../shared/shared'; -import { GetModbusProtocolRequest } from './getModbusProtocolRequest'; -import { GetModbusProtocolResponse } from './getModbusProtocolResponse'; +import { GetModbusProtocolExportXlsxRequest } from './getModbusProtocolExportXlsxRequest'; +import { Base64PayloadResponse } from '../../../../shared/jsonrpc/response/base64PayloadResponse'; @Component({ selector: ModbusApiComponent.SELECTOR, @@ -28,20 +27,28 @@ export class ModbusApiComponent { ) { } ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route); } getModbusProtocol() { this.service.getCurrentEdge().then(edge => { - let request = new ComponentJsonApiRequest({ componentId: this.componentId, payload: new GetModbusProtocolRequest() }); + let request = new ComponentJsonApiRequest({ componentId: this.componentId, payload: new GetModbusProtocolExportXlsxRequest() }); edge.sendRequest(this.websocket, request).then(response => { - let r = response as GetModbusProtocolResponse; - const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(r.result.table); - const workbook: XLSX.WorkBook = { Sheets: { 'data': worksheet }, SheetNames: ['data'] }; - const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); - const data: Blob = new Blob([excelBuffer], { + let r = response as Base64PayloadResponse; + + // decode base64 string, remove space for IE compatibility + // source: https://stackoverflow.com/questions/36036280/base64-representing-pdf-to-blob-javascript/45872086 + var binary = atob(r.result.payload.replace(/\s/g, '')); + var len = binary.length; + var buffer = new ArrayBuffer(len); + var view = new Uint8Array(buffer); + for (var i = 0; i < len; i++) { + view[i] = binary.charCodeAt(i); + } + const data: Blob = new Blob([view], { type: ModbusApiComponent.EXCEL_TYPE }); + const fileName = "Modbus-TCP-" + edge.id; FileSaver.saveAs(data, fileName + ModbusApiComponent.EXCEL_EXTENSION); diff --git a/ui/src/app/edge/live/widgets/production/production.component.html b/ui/src/app/edge/live/widgets/production/production.component.html new file mode 100644 index 00000000000..eb881e43d43 --- /dev/null +++ b/ui/src/app/edge/live/widgets/production/production.component.html @@ -0,0 +1,42 @@ + + + + + + + + General.Production + + + + + + + + + + + + + + + + + + + + + + + + +
    + General.Production DC{{ sum.activePowerDC | number:'1.0-0' }} W
    General.Production + AC + {{ sum.activePowerAC | number:'1.0-0' }} +  W-
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/production/production.component.ts b/ui/src/app/edge/live/widgets/production/production.component.ts new file mode 100644 index 00000000000..ef83488f630 --- /dev/null +++ b/ui/src/app/edge/live/widgets/production/production.component.ts @@ -0,0 +1,39 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ChannelAddress, Edge, Service, Websocket } from '../../../../shared/shared'; + +@Component({ + selector: 'production', + templateUrl: './production.component.html' +}) +export class ProductionComponent { + + private static readonly SELECTOR = "production"; + + public edge: Edge = null; + + constructor( + public service: Service, + private websocket: Websocket, + private route: ActivatedRoute, + ) { } + + ngOnInit() { + this.service.setCurrentComponent('', this.route).then(edge => { + this.edge = edge; + edge.subscribeChannels(this.websocket, ProductionComponent.SELECTOR, [ + // Production + new ChannelAddress('_sum', 'ProductionActivePower'), + new ChannelAddress('_sum', 'ProductionDcActualPower'), + new ChannelAddress('_sum', 'ProductionAcActivePower'), + new ChannelAddress('_sum', 'ProductionMaxActivePower'), + ]); + }); + } + + ngOnDestroy() { + if (this.edge != null) { + this.edge.unsubscribeChannels(this.websocket, ProductionComponent.SELECTOR); + } + } +} diff --git a/ui/src/app/edge/live/widgets/storage/storage.component.html b/ui/src/app/edge/live/widgets/storage/storage.component.html new file mode 100644 index 00000000000..0c685edab6e --- /dev/null +++ b/ui/src/app/edge/live/widgets/storage/storage.component.html @@ -0,0 +1,47 @@ + + + + + + + + General.StorageSystem + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    General.Soc{{ sum.soc | number:'1.0-0' }} %
    General.ChargePower + {{ sum.effectiveChargePower | number:'1.0-0' }} W-
    General.DischargePower + {{ sum.effectiveDischargePower | number:'1.0-0' }} W-
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/edge/live/widgets/storage/storage.component.ts b/ui/src/app/edge/live/widgets/storage/storage.component.ts new file mode 100644 index 00000000000..e5cde3dcbe2 --- /dev/null +++ b/ui/src/app/edge/live/widgets/storage/storage.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ChannelAddress, Edge, Service, Websocket } from '../../../../shared/shared'; + +@Component({ + selector: 'storage', + templateUrl: './storage.component.html' +}) +export class StorageComponent { + + private static readonly SELECTOR = "storage"; + + public edge: Edge = null; + + constructor( + public service: Service, + private websocket: Websocket, + private route: ActivatedRoute, + ) { } + + ngOnInit() { + this.service.setCurrentComponent('', this.route).then(edge => { + this.edge = edge; + edge.subscribeChannels(this.websocket, StorageComponent.SELECTOR, [ + // Ess + new ChannelAddress('_sum', 'EssSoc'), + new ChannelAddress('_sum', 'EssActivePower'), + ]); + }); + } + + ngOnDestroy() { + if (this.edge != null) { + this.edge.unsubscribeChannels(this.websocket, StorageComponent.SELECTOR); + } + } +} diff --git a/ui/src/app/edge/index/widget/widget.component.html b/ui/src/app/edge/live/widgets/widgets.component.html similarity index 51% rename from ui/src/app/edge/index/widget/widget.component.html rename to ui/src/app/edge/live/widgets/widgets.component.html index 281d911ba44..015080dc459 100644 --- a/ui/src/app/edge/index/widget/widget.component.html +++ b/ui/src/app/edge/live/widgets/widgets.component.html @@ -1,10 +1,28 @@ + + + + + + + + + + + + + + + + + + - - - + + + @@ -19,5 +37,10 @@ + + + + + \ No newline at end of file diff --git a/ui/src/app/edge/index/widget/widget.component.ts b/ui/src/app/edge/live/widgets/widgets.component.ts similarity index 64% rename from ui/src/app/edge/index/widget/widget.component.ts rename to ui/src/app/edge/live/widgets/widgets.component.ts index 279c7f582dc..e677558bfe5 100644 --- a/ui/src/app/edge/index/widget/widget.component.ts +++ b/ui/src/app/edge/live/widgets/widgets.component.ts @@ -3,12 +3,12 @@ import { ActivatedRoute } from '@angular/router'; import { Service, Widget } from '../../../shared/shared'; @Component({ - selector: WidgetComponent.SELECTOR, - templateUrl: './widget.component.html' + selector: WidgetsComponent.SELECTOR, + templateUrl: './widgets.component.html' }) -export class WidgetComponent { +export class WidgetsComponent { - private static readonly SELECTOR = "widget"; + private static readonly SELECTOR = "widgets"; public widgets: Widget[] = []; @@ -18,7 +18,7 @@ export class WidgetComponent { ) { } ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent('', this.route); this.service.getWidgets().then(widgets => this.widgets = widgets); } diff --git a/ui/src/app/edge/live/widgets/widgets.module.ts b/ui/src/app/edge/live/widgets/widgets.module.ts new file mode 100644 index 00000000000..f4f6c900233 --- /dev/null +++ b/ui/src/app/edge/live/widgets/widgets.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from './../../../shared/shared.module'; +import { EvcsComponent } from './evcs/evcs.component'; +import { ModbusApiComponent } from './modbusapi/modbusapi.component'; +import { WidgetsComponent } from './widgets.component'; +import { ChannelthresholdComponent } from './channelthreshold/channelthreshold.component'; +import { StorageComponent } from './storage/storage.component'; +import { GridComponent } from './grid/grid.component'; +import { ProductionComponent } from './production/production.component'; +import { ConsumptionComponent } from './consumption/consumption.component'; +import { InfoComponent } from './info/info.component'; + +@NgModule({ + imports: [ + SharedModule + ], + declarations: [ + WidgetsComponent, + ChannelthresholdComponent, + EvcsComponent, + ModbusApiComponent, + StorageComponent, + GridComponent, + ConsumptionComponent, + ProductionComponent, + InfoComponent + ], + exports: [ + WidgetsComponent + ] +}) +export class WidgetsModule { } + + + diff --git a/ui/src/app/edge/settings/channels/channels.component.html b/ui/src/app/edge/settings/channels/channels.component.html new file mode 100644 index 00000000000..5c92fbb9b8a --- /dev/null +++ b/ui/src/app/edge/settings/channels/channels.component.html @@ -0,0 +1,119 @@ + + + + + + + + + + + Channel {{ address.toString() }} + + + + + + + + + + Meta + + Type: {{ channelConfig.type.toLowerCase() }} + / Access-Mode: {{ channelConfig.accessMode }} + / Text:{{ channelConfig.text }} + + + + + + Value + + + {{ currentData.channel[address.toString()] }} {{ channelConfig.unit }} + + + + + ({{ option.key }}) + + + + + + (State is SET) + (State is not set) + + + + (On) + (Off) + + + + + + + Set value + + + + + + + + + + + + + + + + + + + + + + + + + Add Channel + + + + + + Component + + + {{ entry.value.id }} ({{ entry.value.alias }}) + + + + + + Channel + + + + {{ entry.key }} + + + + + + Add + {{ selectedComponentId.value }}/{{ selectedChannelId.value }} + + + + + + \ No newline at end of file diff --git a/ui/src/app/edge/settings/channels/channels.component.ts b/ui/src/app/edge/settings/channels/channels.component.ts new file mode 100644 index 00000000000..2f150a09313 --- /dev/null +++ b/ui/src/app/edge/settings/channels/channels.component.ts @@ -0,0 +1,91 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ChannelAddress, Edge, EdgeConfig, Service, Websocket } from '../../../shared/shared'; +import { IGNORE_NATURES } from '../component/shared/shared'; +import { SetChannelVaLueRequest } from 'src/app/shared/jsonrpc/request/setChannelValueRequest'; + +@Component({ + selector: ChannelsComponent.SELECTOR, + templateUrl: './channels.component.html' +}) +export class ChannelsComponent { + + private static readonly SELECTOR = "channels"; + + public edge: Edge = null; + public config: EdgeConfig = null; + public subscribedChannels: ChannelAddress[] = []; + + constructor( + private service: Service, + private websocket: Websocket, + private route: ActivatedRoute + ) { } + + ngOnInit() { + this.service.setCurrentComponent("Channels" /* TODO translate */, this.route).then(edge => { + this.edge = edge; + }); + this.service.getConfig().then(config => { + this.config = config; + }) + } + + subscribeChannel(componentId: string, channelId: string) { + this.subscribedChannels.forEach((item, index) => { + if (item.componentId === componentId && item.channelId === channelId) { + // had already been in the list + return; + } + }); + + let address = new ChannelAddress(componentId, channelId); + this.subscribedChannels.push(address); + + if (this.config) { + let channelConfig = this.config.getChannel(address); + if (channelConfig) { + if (channelConfig.accessMode == "WO") { + // do not subscribe Write-Only Channels + return; + } + } + } + + if (this.edge) { + this.edge.subscribeChannels(this.websocket, ChannelsComponent.SELECTOR, this.subscribedChannels); + } + } + + unsubscribeChannel(address: ChannelAddress) { + this.subscribedChannels.forEach((item, index) => { + if (item.componentId === address.componentId && item.channelId === address.channelId) { + this.subscribedChannels.splice(index, 1); + } + }); + } + + setChannelValue(address: ChannelAddress, value: any) { + if (this.edge) { + this.edge.sendRequest( + this.service.websocket, + new SetChannelVaLueRequest({ + componentId: address.componentId, + channelId: address.channelId, + value: value + }) + ).then(response => { + this.service.toast("Successfully set " + address.toString() + " to [" + value + "]", "success"); + }).catch(reason => { + this.service.toast("Error setting " + address.toString() + " to [" + value + "]", 'danger'); + }); + } + } + + ngOnDestroy() { + if (this.edge != null) { + this.edge.unsubscribeChannels(this.websocket, ChannelsComponent.SELECTOR); + } + } + +} \ No newline at end of file diff --git a/ui/src/app/edge/settings/component/install/index.component.html b/ui/src/app/edge/settings/component/install/index.component.html index 5ff7a2a9655..7ca40aedfdf 100644 --- a/ui/src/app/edge/settings/component/install/index.component.html +++ b/ui/src/app/edge/settings/component/install/index.component.html @@ -1,11 +1,15 @@ + + + - + {{ nature.name }} - +

    {{ factory.name }}

    diff --git a/ui/src/app/edge/settings/component/install/index.component.ts b/ui/src/app/edge/settings/component/install/index.component.ts index 5a3d03f7097..336312b8731 100644 --- a/ui/src/app/edge/settings/component/install/index.component.ts +++ b/ui/src/app/edge/settings/component/install/index.component.ts @@ -2,6 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Service, Utils, Websocket, EdgeConfig } from '../../../../shared/shared'; import { IGNORE_NATURES } from '../shared/shared'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: IndexComponent.SELECTOR, @@ -13,22 +14,24 @@ export class IndexComponent implements OnInit { public list: { readonly nature: EdgeConfig.Nature, - readonly factories: EdgeConfig.Factory[] + isNatureClicked: Boolean, + readonly allFactories: EdgeConfig.Factory[] + filteredFactories: EdgeConfig.Factory[] }[] = []; + public showAllFactories = false; constructor( private route: ActivatedRoute, - protected utils: Utils, - private websocket: Websocket, private service: Service, + private translate: TranslateService ) { } ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent(this.translate.instant('Edge.Config.Index.AddComponents'), this.route); this.service.getConfig().then(config => { for (let natureId in config.natures) { - if (natureId in IGNORE_NATURES) { + if (IGNORE_NATURES.includes(natureId)) { continue; } @@ -39,10 +42,34 @@ export class IndexComponent implements OnInit { } this.list.push({ nature: nature, - factories: factories + isNatureClicked: false, + allFactories: factories, + filteredFactories: factories }); } + this.updateFilter(""); }); } + updateFilter(completeFilter: string) { + // take each space-separated string as an individual and-combined filter + let filters = completeFilter.split(' '); + let countFilteredFactories = 0; + for (let entry of this.list) { + entry.filteredFactories = entry.allFactories.filter(factory => + // Search for filter strings + Utils.matchAll(filters, [ + factory.id.toLowerCase(), + factory.name.toLowerCase(), + factory.description.toLowerCase()]), + ); + countFilteredFactories += entry.filteredFactories.length; + } + // If not more than 10 Factories survived filtering -> show all of them immediately + if (countFilteredFactories > 10) { + this.showAllFactories = false; + } else { + this.showAllFactories = true; + } + } } \ No newline at end of file diff --git a/ui/src/app/edge/settings/component/install/install.component.ts b/ui/src/app/edge/settings/component/install/install.component.ts index fed585e367f..ca2ffd50c75 100644 --- a/ui/src/app/edge/settings/component/install/install.component.ts +++ b/ui/src/app/edge/settings/component/install/install.component.ts @@ -3,6 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { Service, Utils, Websocket, EdgeConfig, Edge } from '../../../../shared/shared'; import { FormGroup } from '@angular/forms'; import { FormlyFieldConfig } from '@ngx-formly/core'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: ComponentInstallComponent.SELECTOR, @@ -25,11 +26,12 @@ export class ComponentInstallComponent implements OnInit { protected utils: Utils, private websocket: Websocket, private service: Service, + private translate: TranslateService ) { } ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => { + this.service.setCurrentComponent(this.translate.instant('Edge.Config.Index.AddComponents'), this.route).then(edge => { this.edge = edge; }); let factoryId = this.route.snapshot.params["factoryId"]; @@ -45,14 +47,13 @@ export class ComponentInstallComponent implements OnInit { type: 'input', templateOptions: { label: property.name, - description: property.description, - required: property.isRequired, + description: property.description } } // add Property Schema Utils.deepCopy(property.schema, field); fields.push(field); - if (property.defaultValue) { + if (property.defaultValue != null) { model[property_id] = property.defaultValue; } } diff --git a/ui/src/app/edge/settings/component/update/index.component.html b/ui/src/app/edge/settings/component/update/index.component.html index a1191a1a4ec..124cd668ec1 100644 --- a/ui/src/app/edge/settings/component/update/index.component.html +++ b/ui/src/app/edge/settings/component/update/index.component.html @@ -1,16 +1,25 @@ + + + - - {{ nature.name }} - + + {{ nature.name }} + - - -

    {{ entry.component.id }}

    -

    {{ entry.factory.description }}

    -
    -
    + + + + +

    {{ entry.component.id }}

    +

    {{ entry.factory.description }}

    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/edge/settings/component/update/index.component.ts b/ui/src/app/edge/settings/component/update/index.component.ts index dced9ebeba3..75ec2a96b2d 100644 --- a/ui/src/app/edge/settings/component/update/index.component.ts +++ b/ui/src/app/edge/settings/component/update/index.component.ts @@ -2,33 +2,38 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Service, Utils, Websocket, EdgeConfig } from '../../../../shared/shared'; import { IGNORE_NATURES } from '../shared/shared'; +import { TranslateService } from '@ngx-translate/core'; + +interface ListEntry { + readonly component: EdgeConfig.Component + readonly factory: EdgeConfig.Factory +}; @Component({ selector: IndexComponent.SELECTOR, templateUrl: './index.component.html' }) -export class IndexComponent implements OnInit, OnDestroy { +export class IndexComponent implements OnInit { private static readonly SELECTOR = "indexComponentUpdate"; public list: { readonly nature: EdgeConfig.Nature, - readonly entries: { - readonly component: EdgeConfig.Component - readonly factory: EdgeConfig.Factory - }[] + isNatureClicked: Boolean, + readonly allEntries: ListEntry[], + filteredEntries: ListEntry[] }[] = []; + public showAllEntries = false; constructor( private route: ActivatedRoute, - protected utils: Utils, - private websocket: Websocket, private service: Service, + private translate: TranslateService ) { } ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent(this.translate.instant('Edge.Config.Index.AdjustComponents'), this.route); this.service.getConfig().then(config => { for (let natureId in config.natures) { if (IGNORE_NATURES.includes(natureId)) { @@ -45,19 +50,42 @@ export class IndexComponent implements OnInit, OnDestroy { let factory = config.factories[component.factoryId]; entries.push({ component: component, + isFiltered: true, factory: factory }) } this.list.push({ nature: nature, - entries: entries + isNatureClicked: false, + allEntries: entries, + filteredEntries: entries }); } } + this.updateFilter(""); }); } - ngOnDestroy() { + updateFilter(completeFilter: string) { + // take each space-separated string as an individual and-combined filter + let filters = completeFilter.split(' '); + let countFilteredEntries = 0; + for (let entry of this.list) { + entry.filteredEntries = entry.allEntries.filter(entry => + // Search for filter strings in Component-ID, -Alias and Factory-ID + Utils.matchAll(filters, [ + entry.component.id.toLowerCase(), + entry.component.alias.toLowerCase(), + entry.factory.id.toLowerCase()]) + ); + countFilteredEntries += entry.filteredEntries.length; + } + // If not more than 5 Entries survived filtering -> show all of them immediately + if (countFilteredEntries > 5) { + this.showAllEntries = false; + } else { + this.showAllEntries = true; + } } } \ No newline at end of file diff --git a/ui/src/app/edge/settings/component/update/update.component.ts b/ui/src/app/edge/settings/component/update/update.component.ts index ff8646a543a..63f1ff95eac 100644 --- a/ui/src/app/edge/settings/component/update/update.component.ts +++ b/ui/src/app/edge/settings/component/update/update.component.ts @@ -4,6 +4,7 @@ import { Service, Utils, Websocket, EdgeConfig, Edge } from '../../../../shared/ import { FormGroup, FormControl } from '@angular/forms'; import { FormlyFieldConfig } from '@ngx-formly/core'; import { ToastController } from '@ionic/angular'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: ComponentUpdateComponent.SELECTOR, @@ -25,12 +26,13 @@ export class ComponentUpdateComponent implements OnInit { private route: ActivatedRoute, protected utils: Utils, private websocket: Websocket, - private service: Service + private service: Service, + private translate: TranslateService ) { } ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => { + this.service.setCurrentComponent(this.translate.instant('Edge.Config.Index.AdjustComponents'), this.route).then(edge => { this.edge = edge; }); let componentId = this.route.snapshot.params["componentId"]; diff --git a/ui/src/app/edge/settings/settings.component.html b/ui/src/app/edge/settings/settings.component.html index bee682b0217..47add5d1b73 100644 --- a/ui/src/app/edge/settings/settings.component.html +++ b/ui/src/app/edge/settings/settings.component.html @@ -3,11 +3,9 @@ - - - - Edge.Config.Index.Log - + + + Edge.Config.Index.Log Edge.Config.Index.LiveLog @@ -20,12 +18,9 @@ - - - - - Komponenten konfigurieren - + + + Edge.Config.Index.AdjustComponents @@ -37,12 +32,9 @@ - - - - - Komponenten installieren - + + + Edge.Config.Index.AddComponents @@ -52,5 +44,20 @@ + + + + + + Channels + + + + + + + + +
    \ No newline at end of file diff --git a/ui/src/app/edge/settings/settings.component.ts b/ui/src/app/edge/settings/settings.component.ts index 197c307661e..445f5988e99 100644 --- a/ui/src/app/edge/settings/settings.component.ts +++ b/ui/src/app/edge/settings/settings.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, Service, Utils } from '../../shared/shared'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'settings', @@ -14,11 +15,12 @@ export class SettingsComponent implements OnInit { private route: ActivatedRoute, protected utils: Utils, private service: Service, + private translate: TranslateService ) { } ngOnInit() { - this.service.setCurrentEdge(this.route).then(edge => { + this.service.setCurrentComponent(this.translate.instant('Menu.EdgeSettings'), this.route).then(edge => { this.edge = edge }); } diff --git a/ui/src/app/edge/settings/settings.module.ts b/ui/src/app/edge/settings/settings.module.ts index cd3abee3dca..ede8d5365ea 100644 --- a/ui/src/app/edge/settings/settings.module.ts +++ b/ui/src/app/edge/settings/settings.module.ts @@ -1,11 +1,11 @@ import { NgModule } from '@angular/core'; import { SharedModule } from './../../shared/shared.module'; -import { SettingsComponent } from './settings.component'; -import { SystemLogComponent } from './systemlog/systemlog.component'; +import { ChannelsComponent } from './channels/channels.component'; import { IndexComponent as ComponentInstallIndexComponent } from './component/install/index.component'; import { ComponentInstallComponent } from './component/install/install.component'; import { IndexComponent as ComponentUpdateIndexComponent } from './component/update/index.component'; import { ComponentUpdateComponent } from './component/update/update.component'; +import { SettingsComponent } from './settings.component'; @NgModule({ imports: [ @@ -13,7 +13,7 @@ import { ComponentUpdateComponent } from './component/update/update.component'; ], declarations: [ SettingsComponent, - SystemLogComponent, + ChannelsComponent, ComponentInstallIndexComponent, ComponentInstallComponent, ComponentUpdateIndexComponent, diff --git a/ui/src/app/edge/settings/systemlog/systemlog.component.html b/ui/src/app/edge/settings/systemlog/systemlog.component.html index 6c875978494..52a2863f829 100644 --- a/ui/src/app/edge/settings/systemlog/systemlog.component.html +++ b/ui/src/app/edge/settings/systemlog/systemlog.component.html @@ -2,11 +2,9 @@ - + - - System-Log - + System-Log diff --git a/ui/src/app/edge/settings/systemlog/systemlog.component.ts b/ui/src/app/edge/settings/systemlog/systemlog.component.ts index 723e703f42d..64d69841f97 100644 --- a/ui/src/app/edge/settings/systemlog/systemlog.component.ts +++ b/ui/src/app/edge/settings/systemlog/systemlog.component.ts @@ -4,6 +4,7 @@ import { parse } from 'date-fns'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { Service, Utils, Websocket } from '../../../shared/shared'; +import { TranslateCompiler, TranslateService } from '@ngx-translate/core'; @Component({ selector: SystemLogComponent.SELECTOR, @@ -11,7 +12,7 @@ import { Service, Utils, Websocket } from '../../../shared/shared'; }) export class SystemLogComponent implements OnInit, OnDestroy { - private static readonly SELECTOR = "log"; + private static readonly SELECTOR = "systemLog"; public lines: { time: string, @@ -29,11 +30,12 @@ export class SystemLogComponent implements OnInit, OnDestroy { protected utils: Utils, private websocket: Websocket, private service: Service, + private translate: TranslateService ) { } ngOnInit() { - this.service.setCurrentEdge(this.route); + this.service.setCurrentComponent(this.translate.instant('Edge.Config.Index.LiveLog'), this.route); this.subscribe(); } diff --git a/ui/src/app/index/index.component.html b/ui/src/app/index/index.component.html index 63719b51ccf..e8477d88132 100644 --- a/ui/src/app/index/index.component.html +++ b/ui/src/app/index/index.component.html @@ -1,33 +1,33 @@ - + + - - - - - Login - - Bitte geben Sie Ihr Passwort ein oder bestätigen Sie die Voreingabe um sich als Gast anzumelden. - - + + + + Login - -
    - Passwort - -
    -
    - -

    - {{ (websocket.event | async)?.message }} -

    - - - +
    +
    + + + Bitte geben Sie Ihr Passwort ein oder bestätigen Sie die Voreingabe um sich als Gast anzumelden. + + + + Passwort + + + + + +
    +
    +
    @@ -54,7 +54,7 @@

    {{ edge.comment }}

    ID: {{ edge.id }}

    Typ: {{ edge.producttype }}

    -

    Angemeldet als: {{ edge.role }}.

    +

    Angemeldet als: {{ edge.getRoleString() }}.

    diff --git a/ui/src/app/index/index.component.ts b/ui/src/app/index/index.component.ts index f798bfa440e..1e3fa67bf42 100644 --- a/ui/src/app/index/index.component.ts +++ b/ui/src/app/index/index.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -19,22 +19,18 @@ export class IndexComponent { public env = environment; public form: FormGroup; - private filter: string = ''; + public filter: string = ''; + public filteredEdges: Edge[] = []; private stopOnDestroy: Subject = new Subject(); - private filteredEdges: Edge[] = []; private slice: number = 20; constructor( public websocket: Websocket, public utils: Utils, - private translate: TranslateService, - private formBuilder: FormBuilder, private router: Router, - private service: Service) { - this.form = this.formBuilder.group({ - "password": this.formBuilder.control('user') - }); + private service: Service, + private route: ActivatedRoute) { //Forwarding to device index if there is only 1 edge service.edges.pipe(takeUntil(this.stopOnDestroy)).subscribe(edges => { @@ -49,6 +45,10 @@ export class IndexComponent { }) } + ngOnInit() { + this.service.setCurrentComponent('', this.route); + } + updateFilteredEdges() { let filter = this.filter.toLowerCase(); let allEdges = this.service.edges.getValue(); @@ -80,12 +80,11 @@ export class IndexComponent { .map(edgeId => allEdges[edgeId]); } - doLogin() { - let password: string = this.form.value['password']; + doLogin(password: string) { let request = new AuthenticateWithPasswordRequest({ password: password }); this.websocket.sendRequest(request).then(response => { this.handleAuthenticateWithPasswordResponse(response as AuthenticateWithPasswordResponse); - }).then(reason => { + }).catch(reason => { console.error("Error on Login", reason); }) } diff --git a/ui/src/app/settings/settings.component.html b/ui/src/app/settings/settings.component.html index cd2117f8f58..fca5e5c0a49 100644 --- a/ui/src/app/settings/settings.component.html +++ b/ui/src/app/settings/settings.component.html @@ -1,43 +1,35 @@ - - - - - - - Menu.Settings - + + + + Menu.GeneralSettings -
      - About.Language - -
    + + About.Language + + + {{ language }} + + +
    - - - - - - - Debug Mode - + + + + Debug Mode - - - - Debug-Mode - - + + + + Debug-Mode + diff --git a/ui/src/app/settings/settings.component.ts b/ui/src/app/settings/settings.component.ts index 3ac9293c004..d7953b72e41 100644 --- a/ui/src/app/settings/settings.component.ts +++ b/ui/src/app/settings/settings.component.ts @@ -2,7 +2,8 @@ import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { environment } from '../../environments'; import { LanguageTag, Language } from '../shared/translate/language'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Service, Edge } from '../shared/shared'; @Component({ selector: 'settings', @@ -12,11 +13,14 @@ export class SettingsComponent { public env = environment; + public edge: Edge = null public readonly languages: LanguageTag[]; public currentLanguage: LanguageTag; constructor( public translate: TranslateService, + private service: Service, + private route: ActivatedRoute, ) { this.languages = Language.getLanguageTags(); this.currentLanguage = translate.currentLang as LanguageTag; @@ -30,4 +34,10 @@ export class SettingsComponent { this.currentLanguage = language; this.translate.use(language); } + + ngOnInit() { + this.service.setCurrentComponent(this.translate.instant('Menu.GeneralSettings'), this.route).then(edge => { + this.edge = edge + }); + } } diff --git a/ui/src/app/shared/edge/currentdata.ts b/ui/src/app/shared/edge/currentdata.ts index 7f78ae129c1..06e042c6a86 100644 --- a/ui/src/app/shared/edge/currentdata.ts +++ b/ui/src/app/shared/edge/currentdata.ts @@ -26,7 +26,9 @@ export class CurrentData { dischargeActivePowerDC: null, maxDischargeActivePower: null, powerRatio: null, - maxApparentPower: null + maxApparentPower: null, + effectiveChargePower: null, + effectiveDischargePower: null, }, production: { hasDC: false, powerRatio: null, @@ -47,35 +49,6 @@ export class CurrentData { } }; - { - /* - * Storage - * > 0 => Discharge - * < 0 => Charge - */ - result.storage.soc = c['_sum/EssSoc']; - const essActivePower: number = c['_sum/EssActivePower']; - result.storage.maxApparentPower = c['_sum/MaxApparentPower']; - if (!result.storage.maxApparentPower) { - result.storage.maxApparentPower = 5000; - } - result.storage.chargeActivePowerDC = c['_sum/ProductionDcActualPower']; - if (essActivePower == null) { - // keep 'null' - } else if (essActivePower > 0) { - result.storage.chargeActivePowerAC = 0; - result.storage.dischargeActivePowerAC = essActivePower; - // TODO: should consider DC-Power of ratio - result.storage.powerRatio = Utils.orElse(Utils.divideSafely(essActivePower, result.storage.maxApparentPower), 0); - } else { - result.storage.chargeActivePowerAC = Utils.multiplySafely(essActivePower, -1); - result.storage.dischargeActivePowerAC = 0; - result.storage.powerRatio = Utils.orElse(Utils.divideSafely(essActivePower, result.storage.maxApparentPower), 0); - } - result.storage.chargeActivePower = Utils.addSafely(result.storage.chargeActivePowerAC, result.storage.chargeActivePowerDC); - result.storage.dischargeActivePower = result.storage.dischargeActivePowerAC; - } - { /* * Grid @@ -117,11 +90,51 @@ export class CurrentData { result.production.activePowerDC = c['_sum/ProductionDcActualPower']; } + { + /* + * Storage + * > 0 => Discharge + * < 0 => Charge + */ + result.storage.soc = c['_sum/EssSoc']; + const essActivePower: number = c['_sum/EssActivePower']; + result.storage.maxApparentPower = c['_sum/MaxApparentPower']; + + if (!result.storage.maxApparentPower) { + result.storage.maxApparentPower = 5000; + } + result.storage.chargeActivePowerDC = c['_sum/ProductionDcActualPower']; + if (essActivePower == null) { + // keep 'null' + } else if (essActivePower > 0) { + result.storage.chargeActivePowerAC = null; + result.storage.dischargeActivePowerAC = essActivePower; + // TODO: should consider DC-Power of ratio + result.storage.powerRatio = Utils.orElse(Utils.divideSafely(essActivePower, result.storage.maxApparentPower), 0); + } else { + result.storage.chargeActivePowerAC = Utils.multiplySafely(essActivePower, -1); + result.storage.dischargeActivePowerAC = null; + result.storage.powerRatio = Utils.orElse(Utils.divideSafely(essActivePower, result.storage.maxApparentPower), 0); + + } + result.storage.chargeActivePower = Utils.addSafely(result.storage.chargeActivePowerAC, result.storage.chargeActivePowerDC); + result.storage.dischargeActivePower = result.storage.dischargeActivePowerAC; + + let effectivePower = Utils.addSafely(Utils.subtractSafely(result.storage.chargeActivePowerAC, result.storage.dischargeActivePowerAC), result.production.activePowerDC); + if (effectivePower != null) { + if (effectivePower > 0) { + result.storage.effectiveChargePower = effectivePower; + } else { + result.storage.effectiveDischargePower = effectivePower * -1; + } + } + } + { /* * Consumption */ - result.consumption.activePower = Utils.orElse(c['_sum/ConsumptionActivePower'], 0); + result.consumption.activePower = c['_sum/ConsumptionActivePower']; let consumptionMaxActivePower = c['_sum/ConsumptionMaxActivePower']; if (!consumptionMaxActivePower) { consumptionMaxActivePower = 10000; diff --git a/ui/src/app/shared/edge/edge.ts b/ui/src/app/shared/edge/edge.ts index 2711318a1dc..b225dad6bf5 100644 --- a/ui/src/app/shared/edge/edge.ts +++ b/ui/src/app/shared/edge/edge.ts @@ -18,6 +18,7 @@ import { Role } from '../type/role'; import { SystemLog } from '../type/systemlog'; import { CurrentData } from './currentdata'; import { EdgeConfig } from './edgeconfig'; +import { EdgeConfigNotification } from '../jsonrpc/notification/edgeConfigNotification'; export class Edge { @@ -71,7 +72,8 @@ export class Edge { public refreshConfig(websocket: Websocket): void { let request = new GetEdgeConfigRequest(); this.sendRequest(websocket, request).then(response => { - this.config.next(new EdgeConfig(response as GetEdgeConfigResponse)); + let edgeConfigResponse = response as GetEdgeConfigResponse; + this.config.next(new EdgeConfig(edgeConfigResponse.result)); }).catch(reason => { console.warn("refreshConfig got error", reason) // TODO error @@ -86,9 +88,9 @@ export class Edge { * @param id a unique ID for this subscription (e.g. the component selector) * @param channels the subscribed Channel-Adresses */ - public subscribeChannels(websocket: Websocket, id: string, channels: ChannelAddress[]): Promise { + public subscribeChannels(websocket: Websocket, id: string, channels: ChannelAddress[]): void { this.subscribedChannels[id] = channels; - return this.sendSubscribeChannels(websocket); + this.sendSubscribeChannels(websocket); } /** @@ -97,9 +99,9 @@ export class Edge { * @param websocket the Websocket * @param id the unique ID for this subscription */ - public unsubscribeChannels(websocket: Websocket, id: string): Promise { + public unsubscribeChannels(websocket: Websocket, id: string): void { delete this.subscribedChannels[id]; - return this.sendSubscribeChannels(websocket); + this.sendSubscribeChannels(websocket); } /** @@ -125,14 +127,30 @@ export class Edge { * * @param websocket the Websocket */ - private sendSubscribeChannels(websocket: Websocket): Promise { - // merge channels from currentDataSubscribes - let channels: ChannelAddress[] = []; - for (let componentId in this.subscribedChannels) { - channels.push.apply(channels, this.subscribedChannels[componentId]); + private sendSubscribeChannels(websocket: Websocket): void { + // make sure to send not faster than every 100 ms + if (this.subscribeChannelsTimeout == null) { + this.subscribeChannelsTimeout = setTimeout(() => { + // reset subscribeChannelsTimeout + this.subscribeChannelsTimeout = null; + + // merge channels from currentDataSubscribes + let channels: ChannelAddress[] = []; + for (let componentId in this.subscribedChannels) { + channels.push.apply(channels, this.subscribedChannels[componentId]); + } + let request = new SubscribeChannelsRequest(channels); + this.sendRequest(websocket, request); + }, 100); } - let request = new SubscribeChannelsRequest(channels); - return this.sendRequest(websocket, request); + } + private subscribeChannelsTimeout: any = null; + + /** + * Handles a EdgeConfigNotification + */ + public handleEdgeConfigNotification(message: EdgeConfigNotification): void { + this.config.next(new EdgeConfig(message.params)); } /** @@ -256,4 +274,13 @@ export class Edge { public roleIsAtLeast(role: Role | string): boolean { return Role.isAtLeast(this.role, role); } + + /** + * Gets the Role of the Edge as a human-readable string. + * + * @returns the name of the role + */ + public getRoleString(): string { + return Role[this.role].toLowerCase(); + } } \ No newline at end of file diff --git a/ui/src/app/shared/edge/edgeconfig.ts b/ui/src/app/shared/edge/edgeconfig.ts index 59a8d9089f9..b50c21b3415 100644 --- a/ui/src/app/shared/edge/edgeconfig.ts +++ b/ui/src/app/shared/edge/edgeconfig.ts @@ -1,11 +1,12 @@ import { GetEdgeConfigResponse } from "../jsonrpc/response/getEdgeConfigResponse"; +import { ChannelAddress } from '../type/channeladdress'; export class EdgeConfig { - constructor(source?: GetEdgeConfigResponse) { + constructor(source?: EdgeConfig) { if (source) { - this.components = source.result.components; - this.factories = source.result.factories; + this.components = source.components; + this.factories = source.factories; } // initialize Components @@ -174,16 +175,38 @@ export class EdgeConfig { return {}; } } + + /** + * Get Channel. + * + * @param address the ChannelAddress + */ + public getChannel(address: ChannelAddress): EdgeConfig.ComponentChannel { + let component = this.components[address.componentId]; + if (component) { + return component.channels[address.channelId]; + } else { + return null; + } + } } export module EdgeConfig { + export class ComponentChannel { + public readonly type: "BOOLEAN" | "SHORT" | "INTEGER" | "LONG" | "FLOAT" | "DOUBLE" | "STRING"; + public readonly accessMode: "RO" | "RW" | "WO"; + public readonly unit: string; + public readonly category: "OPENEMS_TYPE" | "ENUM" | "STATE"; + } export class Component { public id: string = ""; + public alias: string = ""; constructor( public readonly factoryId: string = "", - public readonly properties: { [key: string]: any } = {} + public readonly properties: { [key: string]: any } = {}, + public readonly channels: { [channelId: string]: ComponentChannel } = {} ) { } } @@ -202,6 +225,7 @@ export module EdgeConfig { constructor( public readonly name: string, + public readonly description: string, public readonly natureIds: string[] = [], public readonly properties: FactoryProperty[] = [] ) { } diff --git a/ui/src/app/shared/jsonrpc/notification/edgeConfigNotification.ts b/ui/src/app/shared/jsonrpc/notification/edgeConfigNotification.ts new file mode 100644 index 00000000000..4000bb27723 --- /dev/null +++ b/ui/src/app/shared/jsonrpc/notification/edgeConfigNotification.ts @@ -0,0 +1,25 @@ +import { JsonrpcNotification } from "../base"; +import { EdgeConfig } from '../../edge/edgeconfig'; + +/** + * Represents a JSON-RPC Notification for new EdgeConfig. + * + *
    + * {
    + *   "jsonrpc": "2.0",
    + *   "method": "edgeConfig",
    + *   "params": EdgeConfig
    + * }
    + * 
    + */ +export class EdgeConfigNotification extends JsonrpcNotification { + + public static readonly METHOD: string = "edgeConfig"; + + public constructor( + public readonly params: EdgeConfig + ) { + super(EdgeConfigNotification.METHOD, params); + } + +} \ No newline at end of file diff --git a/ui/src/app/shared/jsonrpc/request/setChannelValueRequest.ts b/ui/src/app/shared/jsonrpc/request/setChannelValueRequest.ts new file mode 100644 index 00000000000..d08a4bbce57 --- /dev/null +++ b/ui/src/app/shared/jsonrpc/request/setChannelValueRequest.ts @@ -0,0 +1,33 @@ +import { JsonrpcRequest } from "../base"; + +/** + * Sets the write value of a Channel. + * + *
    + * {
    + *   "jsonrpc": "2.0",
    + *   "id": UUID,
    + *   "method": "setChannelValue",
    + *   "params": {
    + *     "componentId": string,
    + *     "channelId": string,
    + *     "value": any
    + *   }
    + * }
    + * 
    + */ +export class SetChannelVaLueRequest extends JsonrpcRequest { + + static METHOD: string = "setChannelValue"; + + public constructor( + public readonly params: { + componentId: string, + channelId: string, + value: any + } + ) { + super(SetChannelVaLueRequest.METHOD, params); + } + +} \ No newline at end of file diff --git a/ui/src/app/shared/jsonrpc/response/base64PayloadResponse.ts b/ui/src/app/shared/jsonrpc/response/base64PayloadResponse.ts new file mode 100644 index 00000000000..cfc8624fe28 --- /dev/null +++ b/ui/src/app/shared/jsonrpc/response/base64PayloadResponse.ts @@ -0,0 +1,26 @@ +import { JsonrpcResponseSuccess } from "../base"; + +/** + * Wraps a JSON-RPC Response with a Base64 encoded payload. + * + *
    + * {
    + *   "jsonrpc": "2.0",
    + *   "id": "UUID",
    + *   "result": {
    + *     "payload": Base64-String
    + *   }
    + * }
    + * 
    + */ +export class Base64PayloadResponse extends JsonrpcResponseSuccess { + + public constructor( + public readonly id: string, + public readonly result: { + payload: string + } + ) { + super(id, result); + } +} \ No newline at end of file diff --git a/ui/src/app/shared/popover/popover.component.html b/ui/src/app/shared/popover/popover.component.html deleted file mode 100644 index 033cbcc6a82..00000000000 --- a/ui/src/app/shared/popover/popover.component.html +++ /dev/null @@ -1,11 +0,0 @@ - - - Menu.AboutUI - - - Menu.Settings - - - Menu.Logout - - \ No newline at end of file diff --git a/ui/src/app/shared/popover/popover.component.scss b/ui/src/app/shared/popover/popover.component.scss deleted file mode 100644 index e1746bcbb96..00000000000 --- a/ui/src/app/shared/popover/popover.component.scss +++ /dev/null @@ -1,4 +0,0 @@ - scroll-content { - overflow-y:hidden; - overflow-x:hidden; -} \ No newline at end of file diff --git a/ui/src/app/shared/popover/popover.component.ts b/ui/src/app/shared/popover/popover.component.ts deleted file mode 100644 index cd8129f673e..00000000000 --- a/ui/src/app/shared/popover/popover.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { PopoverController } from '@ionic/angular'; - -import { environment } from '../../../environments'; -import { Router, RouterModule } from '@angular/router'; -import { Websocket } from '../service/websocket'; - -@Component({ - selector: 'popover', - templateUrl: './popover.component.html', - styleUrls: ['./popover.component.scss'], -}) -export class PopoverPage implements OnInit { - - public env = environment; - - - constructor( - public popoverController: PopoverController, - public websocket: Websocket, - public router: Router - ) { } - - ngOnInit() { - } - - close() { - this.popoverController.dismiss(); - } - -} diff --git a/ui/src/app/shared/popover/popover.module.ts b/ui/src/app/shared/popover/popover.module.ts deleted file mode 100644 index 3a2379fb153..00000000000 --- a/ui/src/app/shared/popover/popover.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; - -import { IonicModule } from '@ionic/angular'; - -import { PopoverPage } from './popover.component'; -import { SharedModule } from '../shared.module'; - - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - IonicModule, - SharedModule - ], - declarations: [PopoverPage] -}) -export class PopoverPageModule { } diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts index d6257ce109f..9288b8491ce 100644 --- a/ui/src/app/shared/service/defaulttypes.ts +++ b/ui/src/app/shared/service/defaulttypes.ts @@ -30,7 +30,9 @@ export module DefaultTypes { dischargeActivePowerDC: number, maxDischargeActivePower?: number, powerRatio: number, - maxApparentPower: number + maxApparentPower: number, + effectiveChargePower: number, + effectiveDischargePower: number, }, production: { powerRatio: number, hasDC: boolean, diff --git a/ui/src/app/shared/service/service.ts b/ui/src/app/shared/service/service.ts index 2faffe1bdec..a8057873f8c 100644 --- a/ui/src/app/shared/service/service.ts +++ b/ui/src/app/shared/service/service.ts @@ -2,7 +2,7 @@ import { ErrorHandler, Injectable } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Cookie } from 'ng2-cookies'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject, Subject, Subscription } from 'rxjs'; import { filter, first, map } from 'rxjs/operators'; import { Edge } from '../edge/edge'; import { EdgeConfig } from '../edge/edgeconfig'; @@ -20,6 +20,16 @@ export class Service implements ErrorHandler { public notificationEvent: Subject = new Subject(); + /** + * Holds the currenty selected Page Title. + */ + public currentPageTitle: string; + + /** + * Holds the current Activated Route + */ + private currentActivatedRoute: ActivatedRoute = null; + /** * Holds the currently selected Edge. */ @@ -106,46 +116,62 @@ export class Service implements ErrorHandler { /** * Parses the route params and sets the current edge */ - public setCurrentEdge(activatedRoute: ActivatedRoute): Promise { + public setCurrentComponent(currentPageTitle: string, activatedRoute: ActivatedRoute): Promise { return new Promise((resolve, reject) => { + // Set the currentPageTitle only once per ActivatedRoute + if (this.currentActivatedRoute != activatedRoute) { + if (currentPageTitle == null || currentPageTitle.trim() === '') { + this.currentPageTitle = 'OpenEMS UI'; + } else { + this.currentPageTitle = currentPageTitle; + } + } + this.currentActivatedRoute = activatedRoute; + + // Get Edge-ID. If not existing -> resolve null let route = activatedRoute.snapshot; let edgeId = route.params["edgeId"]; + if (edgeId == null) { + resolve(null); + } - let timeout = setTimeout(() => { - if (edgeId != null) { - // Timeout: redirect to index - this.router.navigate(['/index']); + let subscription: Subscription = null; + let onError = () => { + if (subscription != null) { + subscription.unsubscribe(); } - subscription.unsubscribe(); setCurrentEdge.apply(null); + // redirect to index + this.router.navigate(['/index']); + } + + let timeout = setTimeout(() => { + console.error("Timeout while setting current edge"); + // onError(); }, Service.TIMEOUT); let setCurrentEdge = (edge: Edge) => { - if (edge != null) { - if (edge != this.currentEdge.value) { + clearTimeout(timeout); + if (edge != this.currentEdge.value) { + if (edge != null) { edge.markAsCurrentEdge(this.websocket); } - } - if (edge != this.currentEdge.value) { this.currentEdge.next(edge); } resolve(edge); } - let subscription = this.edges + subscription = this.edges .pipe( filter(edges => edgeId in edges), first(), map(edges => edges[edgeId]) ) .subscribe(edge => { - clearTimeout(timeout); setCurrentEdge(edge); - }, error => { - clearTimeout(timeout); console.error("Error while setting current edge: ", error); - setCurrentEdge(null); + onError(); }) }); } diff --git a/ui/src/app/shared/service/utils.ts b/ui/src/app/shared/service/utils.ts index d4e0718ab3b..60041bf2c15 100644 --- a/ui/src/app/shared/service/utils.ts +++ b/ui/src/app/shared/service/utils.ts @@ -243,4 +243,26 @@ export class Utils { return v; } } + + /** + * Matches all filter-strings with all base-strings. + * + * @param filters array of filter-strings + * @param bases array of base-strings + * @returns true if all filter strings exist in any base-strings + */ + public static matchAll(filters: string[], bases: string[]): Boolean { + for (let filter of filters) { + let filterMatched = false; + for (let base of bases) { + if (base.includes(filter)) { + filterMatched = true; + } + } + if (!filterMatched) { + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/ui/src/app/shared/service/websocket.ts b/ui/src/app/shared/service/websocket.ts index 45499c6b376..69fe14f1285 100644 --- a/ui/src/app/shared/service/websocket.ts +++ b/ui/src/app/shared/service/websocket.ts @@ -15,6 +15,7 @@ import { Service } from './service'; import { WsData } from './wsdata'; import { SystemLogNotification } from '../jsonrpc/notification/systemLogNotification'; import { SubscribeSystemLogRequest } from '../jsonrpc/request/subscribeSystemLogRequest'; +import { EdgeConfigNotification } from '../jsonrpc/notification/edgeConfigNotification'; @Injectable() export class Websocket { @@ -268,6 +269,10 @@ export class Websocket { let message = edgeRpcNotification.params.payload; switch (message.method) { + case EdgeConfigNotification.METHOD: + this.handleEdgeConfigNotification(edgeId, message as EdgeConfigNotification); + break; + case CurrentDataNotification.METHOD: this.handleCurrentDataNotification(edgeId, message as CurrentDataNotification); break; @@ -278,6 +283,20 @@ export class Websocket { } } + /** + * Handles a EdgeConfigNotification. + * + * @param message + */ + private handleEdgeConfigNotification(edgeId: string, message: EdgeConfigNotification): void { + let edges = this.service.edges.getValue(); + + if (edgeId in edges) { + let edge = edges[edgeId]; + edge.handleEdgeConfigNotification(message); + } + } + /** * Handles a CurrentDataNotification. * diff --git a/ui/src/app/shared/translate/cz.ts b/ui/src/app/shared/translate/cz.ts index bde798f4a16..5b0f8ec82f0 100644 --- a/ui/src/app/shared/translate/cz.ts +++ b/ui/src/app/shared/translate/cz.ts @@ -11,6 +11,7 @@ export const TRANSLATION = { Power: "Výkon", StorageSystem: "Systém bateriového úložiště", History: "Historie", + Live: 'Live', NoValue: "Žádná hodnota", Soc: "Stav nabití", Percentage: "Procentuální vyjádření", @@ -28,12 +29,16 @@ export const TRANSLATION = { Friday: "Pátek", Saturday: "Sobota", Sunday: "Neděle" - } + }, + ReportValue: "Nahlášení poškozených dat" }, Menu: { Index: "Přehled", AboutUI: "About OpenEMS UI", - Settings: 'Obecné Nastavení', + GeneralSettings: 'Obecné Nastavení', + EdgeSettings: 'FEMS Obecné Nastavení', + Menu: 'Menu', + Overview: 'FEMS Overvire', Logout: 'Odhlásit' }, Index: { @@ -84,6 +89,7 @@ export const TRANSLATION = { CarFull: "Auto je plné", EnergieSinceBeginning: "Energie od začátku nabíjení", ChargeMode: "režim načítání", + ActivateCharging: "Aktivujte nabíjecí stanici", NoConnection: { Description: "Nelze jej připojit k nabíjecí stanici.", Help1: "Zkontrolujte, zda je nabíjecí stanice zapnutá a zda je dostupná prostřednictvím sítě", @@ -93,8 +99,13 @@ export const TRANSLATION = { Name: "Optimalizované zatížení", ShortName: "optimalizované", Info: "V tomto režimu je zatížení vozidla přizpůsobeno aktuální výrobě a spotřebě.", - MinInfo: "Chcete-li zabránit nabíjení vozu při nízké paměti v noci, můžete nastavit minimální nabíjení.", - MinCharging: "Garance minimálního poplatku?" + MinInfo: "Pokud chcete zabránit tomu, aby se auto nenabíjelo v noci, můžete nastavit minimální poplatek.", + MinCharging: "Garance minimálního poplatku?", + ChargingPriority: { + Info: "V závislosti na prioritizaci bude vybraná komponenta načtena jako první", + Car: "Car", + Storage: "Storage" + } }, ForceChargeMode: { Name: "Nucené nakládání", @@ -126,6 +137,8 @@ export const TRANSLATION = { ExecuteSimulator: "Zahájit simulaci", Log: "Log", LiveLog: "Live log systému", + AddComponents: "Komponenten installieren", + AdjustComponents: "Komponenten konfigurieren", ManualControl: "Manuální ovládání", DataStorage: "Ukládání dat" }, diff --git a/ui/src/app/shared/translate/de.ts b/ui/src/app/shared/translate/de.ts index eade4c6f010..0d396aa5f8d 100644 --- a/ui/src/app/shared/translate/de.ts +++ b/ui/src/app/shared/translate/de.ts @@ -1,3 +1,5 @@ +import { Service } from '../service/service'; + export const TRANSLATION = { General: { Cumulative: "Kumulierte Werte", @@ -11,6 +13,7 @@ export const TRANSLATION = { Power: "Leistung", StorageSystem: "Speichersystem", History: "Historie", + Live: 'Live', NoValue: "Kein Wert", Soc: "Ladezustand", Percentage: "Prozent", @@ -29,12 +32,16 @@ export const TRANSLATION = { Friday: "Freitag", Saturday: "Samstag", Sunday: "Sonntag" - } + }, + ReportValue: "Fehlerhafte Daten melden" }, Menu: { Index: "Übersicht", AboutUI: "Über OpenEMS UI", - Settings: 'Allgemeine Einstellungen', + GeneralSettings: 'Allgemeine Einstellungen', + EdgeSettings: 'FEMS Einstellungen', + Menu: 'Menü', + Overview: 'FEMS Übersicht', Logout: 'Abmelden' }, Index: { @@ -87,6 +94,7 @@ export const TRANSLATION = { CarFull: "Auto ist voll", EnergieSinceBeginning: "Energie seit Beginn der Ladung", ChargeMode: "Belademodus", + ActivateCharging: "Aktivieren der Ladesäule", NoConnection: { Description: "Es konnte keine Verbindung zur Ladestation aufgebaut werden.", Help1: "Prüfen sie ob die Ladestation eingeschaltet und über das Netz erreichbar ist", @@ -96,8 +104,13 @@ export const TRANSLATION = { Name: "Optimierte Beladung", ShortName: "Optimiert", Info: "In diesem Modus wird die Beladung des Autos an die aktuelle Produktion und den aktuellen Verbrauch angepasst.", - MinInfo: "Falls verhindert werden soll, dass das Auto bei leerem Speicher, in der Nacht gar nicht lädt, kann eine minimale Aufladung festgelegt werden.", - MinCharging: "Minimale Aufladung garantieren?" + MinInfo: "Falls verhindert werden soll, dass das Auto in der Nacht gar nicht lädt, kann eine minimale Aufladung festgelegt werden.", + MinCharging: "Minimale Aufladung garantieren?", + ChargingPriority: { + Info: "Je nach Priorisierung wird die ausgewählte Komponente zuerst beladen", + Car: "Auto", + Storage: "Speicher" + } }, ForceChargeMode: { Name: "Erzwungene Beladung", @@ -129,6 +142,8 @@ export const TRANSLATION = { ExecuteSimulator: "Simulationen ausführen", Log: "Log", LiveLog: "Live Systemprotokoll", + AddComponents: "Komponenten installieren", + AdjustComponents: "Komponenten konfigurieren", ManualControl: "Manuelle Steuerung", DataStorage: "Datenspeicher" }, diff --git a/ui/src/app/shared/translate/en.ts b/ui/src/app/shared/translate/en.ts index 72cec95618a..ff68c43f4bd 100644 --- a/ui/src/app/shared/translate/en.ts +++ b/ui/src/app/shared/translate/en.ts @@ -11,6 +11,7 @@ export const TRANSLATION = { Power: "Power", StorageSystem: "Storage System", History: "History", + Live: 'Live', NoValue: "No value", Soc: "State of charge", Percentage: "Percentage", @@ -29,12 +30,16 @@ export const TRANSLATION = { Friday: "Friday", Saturday: "Saturday", Sunday: "Sunday" - } + }, + ReportValue: "Report corrupted data" }, Menu: { Index: "Index", AboutUI: "About OpenEMS UI", - Settings: 'General Settings', + GeneralSettings: 'General Settings', + EdgeSettings: 'FEMS Settings', + Menu: 'Menu', + Overview: 'FEMS Overview', Logout: 'Sign Out' }, Index: { @@ -87,6 +92,7 @@ export const TRANSLATION = { CarFull: "Car is full", EnergieSinceBeginning: "Energy since the begin of charge", ChargeMode: "Charge Mode", + ActivateCharging: "Activate the charging station", NoConnection: { Description: "No connection to the charging station.", Help1: "Check if the charging station is switched on and can be reached via the network.", @@ -97,7 +103,12 @@ export const TRANSLATION = { ShortName: "Optimized", Info: "In this mode, the load of the car is adjusted to the current production and consumption.", MinInfo: "If you want to prevent that the car is not charging at the night, you could set a minimum charge.", - MinCharging: "Guarantee minimum charge?" + MinCharging: "Guarantee minimum charge?", + ChargingPriority: { + Info: "Depending on the prioritization, the selected component will be loaded first", + Car: "Car", + Storage: "Storage" + } }, ForceChargeMode: { Name: "Force charging", @@ -129,6 +140,8 @@ export const TRANSLATION = { ExecuteSimulator: "Execute simulations", Log: "Log", LiveLog: "Live system log", + AddComponents: "Install components", + AdjustComponents: "Configure components", ManualControl: "Manual control", DataStorage: "Data Storage" }, diff --git a/ui/src/app/shared/translate/es.ts b/ui/src/app/shared/translate/es.ts index fdf1716d557..58b7bef13a5 100644 --- a/ui/src/app/shared/translate/es.ts +++ b/ui/src/app/shared/translate/es.ts @@ -11,6 +11,7 @@ export const TRANSLATION = { Power: "Rendimiento", StorageSystem: "Almacenamiento", History: "Historia", + Live: 'Live', NoValue: "Sin valor", Soc: "Cargo", Percentage: "Por ciento", @@ -29,12 +30,16 @@ export const TRANSLATION = { Friday: "Viernes", Saturday: "Sábado", Sunday: "Domingo" - } + }, + ReportValue: "Reportar datos corruptos" }, Menu: { Index: "Visión general", AboutUI: "Sobre OpenEMS-UI", - Settings: 'Configuración general', + GeneralSettings: 'Configuración general', + EdgeSettings: 'Configuración FEMS', + Menu: 'Menú', + Overview: 'estudio FEMS', Logout: 'Desuscribirse' }, Index: { @@ -87,6 +92,7 @@ export const TRANSLATION = { CarFull: "El carro esta lleno", EnergieSinceBeginning: "Energía desde el inicio de la carga.", ChargeMode: "Modo de carga", + ActivateCharging: "Activar la estación de carga.", NoConnection: { Description: "No se pudo conectar a la estación de carga.", Help1: "Compruebe si la estación de carga está encendida y se puede acceder a ella a través de la red", @@ -96,8 +102,9 @@ export const TRANSLATION = { Name: "Carga optimizada", ShortName: "Optimizado", Info: "En este modo, la carga del automóvil se ajusta a la producción y consumo actuales.", - MinInfo: "Si desea evitar que el automóvil se cargue con poca memoria por la noche, se puede establecer una carga mínima.", - MinCharging: "Garantía de carga mínima?" + MinInfo: "Si desea evitar que el automóvil no se cargue por la noche, puede establecer un cargo mínimo.", + MinCharging: "Garantía de carga mínima?", + ChargingPriority: "Dependiendo de la priorización, el componente seleccionado se cargará primero" }, ForceChargeMode: { Name: "Carga forzada", @@ -129,6 +136,8 @@ export const TRANSLATION = { ExecuteSimulator: "Ejecutar simulaciones", Log: "Registro", LiveLog: "Protocolos de sistema de vida", + AddComponents: "Instalar componentes", + AdjustComponents: "Configurar componentes", ManualControl: "Control manual", DataStorage: "Almacenamiento de datos" }, diff --git a/ui/src/app/shared/translate/nl.ts b/ui/src/app/shared/translate/nl.ts index 0c218145298..eca7737a2d9 100644 --- a/ui/src/app/shared/translate/nl.ts +++ b/ui/src/app/shared/translate/nl.ts @@ -11,6 +11,7 @@ export const TRANSLATION = { Power: "Vermogen", StorageSystem: "Batterij", History: "Historie", + Live: 'Live', NoValue: "Geen waarde", Soc: "Laadstatus", Percentage: "Procent", @@ -28,12 +29,16 @@ export const TRANSLATION = { Friday: "Vrijdag", Saturday: "Zaterdag", Sunday: "Zondag" - } + }, + ReportValue: "Rapporteer beschadigde gegevens" }, Menu: { Index: "Overzicht", AboutUI: "Over OpenEMS UI", - Settings: 'Algemene instellingen', + GeneralSettings: 'Algemene instellingen', + EdgeSettings: 'FEMS instellingen', + Menu: 'Menu', + Overview: 'FEMS overzicht', Logout: 'Uitloggen' }, Index: { @@ -84,6 +89,7 @@ export const TRANSLATION = { CarFull: "Auto is vol", EnergieSinceBeginning: "Energie sinds het begin van de lading", ChargeMode: "laadmodus", + ActivateCharging: "Activeer het laadstation", NoConnection: { Description: "Hij kon niet op het laadstation worden aangesloten.", Help1: "Controleer of het laadstation is ingeschakeld en via het netwerk kan worden bereikt", @@ -93,8 +99,9 @@ export const TRANSLATION = { Name: "Geoptimaliseerd laden", ShortName: "geoptimaliseerde", Info: "In deze modus wordt de belasting van de auto aangepast aan de huidige productie en het huidige verbruik.", - MinInfo: "Als u wilt voorkomen dat de auto 's nachts in het lage geheugen wordt geladen, kan een minimale lading worden ingesteld.", - MinCharging: "Minimale vergoeding betalen?" + MinInfo: "Als u wilt voorkomen dat de auto 's nachts niet oplaadt, kunt u een minimale lading instellen.", + MinCharging: "Minimale vergoeding betalen?", + ChargingPriority: "Afhankelijk van de prioriteit, wordt het geselecteerde onderdeel eerst geladen" }, ForceChargeMode: { Name: "Gedwongen laden", @@ -126,6 +133,8 @@ export const TRANSLATION = { ExecuteSimulator: "Simulatie uitvoeren", Log: "Log", LiveLog: "Live System log", + AddComponents: "Componenten installeren", + AdjustComponents: "Componenten configureren", ManualControl: "Handmatige bediening", DataStorage: "Gegevensopslag" }, diff --git a/ui/src/index.html b/ui/src/index.html index 95c3308e0e2..27af17af0f3 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -1,29 +1,32 @@ - + - + OpenEMS - - + + + + + - + - - - + + + - + + + - - diff --git a/ui/src/karma.conf.js b/ui/src/karma.conf.js index 3ee463a7e63..ffd7c16c8d6 100644 --- a/ui/src/karma.conf.js +++ b/ui/src/karma.conf.js @@ -16,7 +16,7 @@ module.exports = function (config) { clearContext: false // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../coverage/openems'), + dir: require('path').join(__dirname, '../coverage'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, diff --git a/ui/src/main.ts b/ui/src/main.ts index c7b673cf44b..91ec6da5f07 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -9,4 +9,4 @@ if (environment.production) { } platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); + .catch(err => console.log(err)); diff --git a/ui/src/polyfills.ts b/ui/src/polyfills.ts index 48f4aeb437a..65d95061aa7 100644 --- a/ui/src/polyfills.ts +++ b/ui/src/polyfills.ts @@ -55,6 +55,7 @@ import 'classlist.js'; // Run `npm install --save classlist.js`. /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ +import './zone-flags.ts'; import 'zone.js/dist/zone'; // Included with Angular CLI. diff --git a/ui/src/theme/variables.scss b/ui/src/theme/variables.scss index 719e1729a4f..b0b9b69c9fa 100644 --- a/ui/src/theme/variables.scss +++ b/ui/src/theme/variables.scss @@ -74,4 +74,6 @@ --ion-color-dark-contrast-rgb: 255,255,255; --ion-color-dark-shade: #1e1e1e; --ion-color-dark-tint: #383838; + + } \ No newline at end of file diff --git a/ui/src/tsconfig.spec.json b/ui/src/tsconfig.spec.json index de7733630eb..c1b4dd64905 100644 --- a/ui/src/tsconfig.spec.json +++ b/ui/src/tsconfig.spec.json @@ -9,6 +9,7 @@ }, "files": [ "test.ts", + "zone-flags.ts", "polyfills.ts" ], "include": [ diff --git a/ui/src/tslint.json b/ui/src/tslint.json new file mode 100644 index 00000000000..a370e01f5e8 --- /dev/null +++ b/ui/src/tslint.json @@ -0,0 +1,18 @@ +{ + "extends": "../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "page", + "kebab-case" + ] + } +} diff --git a/ui/src/zone-flags.ts b/ui/src/zone-flags.ts new file mode 100644 index 00000000000..e999ae9d110 --- /dev/null +++ b/ui/src/zone-flags.ts @@ -0,0 +1,5 @@ +/** + * Prevents Angular change detection from + * running with certain Web Component callbacks + */ +(window as any).__Zone_disable_customElements = true; diff --git a/ui/tsconfig.json b/ui/tsconfig.json index b271fd9f3d5..759b42bbfd6 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -1,6 +1,14 @@ { "compileOnSave": false, "compilerOptions": { + "paths": { + "core-js/es7/reflect": [ + "node_modules/core-js/proposals/reflect-metadata" + ], + "core-js/es6/*": [ + "node_modules/core-js/es/*" + ] + }, "baseUrl": "./", "outDir": "./dist/out-tsc", "sourceMap": true, @@ -19,4 +27,4 @@ "dom" ] } -} +} \ No newline at end of file