11
11
import org .slf4j .Logger ;
12
12
import org .slf4j .LoggerFactory ;
13
13
14
+ import com .google .common .collect .Lists ;
15
+
14
16
import io .openems .common .exceptions .OpenemsError .OpenemsNamedException ;
15
17
import io .openems .common .exceptions .OpenemsException ;
16
18
import io .openems .edge .bridge .modbus .api .ElementToChannelConverter ;
22
24
import io .openems .edge .common .component .OpenemsComponent ;
23
25
import io .openems .edge .common .event .EdgeEventConstants ;
24
26
import io .openems .edge .common .taskmanager .Priority ;
27
+ import io .openems .edge .meter .api .AsymmetricMeter ;
25
28
import io .openems .edge .meter .api .MeterType ;
26
29
import io .openems .edge .meter .api .SymmetricMeter ;
27
30
import io .openems .edge .pvinverter .api .ManagedSymmetricPvInverter ;
28
31
29
- public abstract class AbstractSunSpecPvInverter extends AbstractOpenemsSunSpecComponent
30
- implements SunSpecPvInverter , ManagedSymmetricPvInverter , SymmetricMeter , OpenemsComponent , EventHandler {
32
+ public abstract class AbstractSunSpecPvInverter extends AbstractOpenemsSunSpecComponent implements SunSpecPvInverter ,
33
+ ManagedSymmetricPvInverter , AsymmetricMeter , SymmetricMeter , OpenemsComponent , EventHandler {
31
34
32
35
private final Logger log = LoggerFactory .getLogger (AbstractSunSpecPvInverter .class );
33
36
private final SetPvLimitHandler setPvLimitHandler = new SetPvLimitHandler (this );
34
37
38
+ private boolean isSinglePhase ;
39
+ private Phase phase ;
40
+
35
41
public AbstractSunSpecPvInverter (Map <SunSpecModel , Priority > activeModels ,
36
42
io .openems .edge .common .channel .ChannelId [] firstInitialChannelIds ,
37
43
io .openems .edge .common .channel .ChannelId []... furtherInitialChannelIds ) throws OpenemsException {
@@ -42,16 +48,42 @@ public AbstractSunSpecPvInverter(Map<SunSpecModel, Priority> activeModels,
42
48
/**
43
49
* Make sure to call this method from the inheriting OSGi Component.
44
50
*
51
+ * @param context ComponentContext of this component. Receive it
52
+ * from parameter for @Activate
53
+ * @param id ID of this component. Typically 'config.id()'
54
+ * @param alias Human-readable name of this Component. Typically
55
+ * 'config.alias()'. Defaults to 'id' if empty
56
+ * @param enabled Whether the component should be enabled.
57
+ * Typically 'config.enabled()'
58
+ * @param unitId Unit-ID of the Modbus target
59
+ * @param cm An instance of ConfigurationAdmin. Receive it
60
+ * using @Reference
61
+ * @param modbusReference The name of the @Reference setter method for the
62
+ * Modbus bridge - e.g. 'Modbus' if you have a
63
+ * setModbus()-method
64
+ * @param modbusId The ID of the Modbus bridge. Typically
65
+ * 'config.modbus_id()'
66
+ * @param readFromCommonBlockNo the starting block number
67
+ * @param phase the phase the inverter is connected
68
+ * @return true if the target filter was updated. You may use it to abort the
69
+ * activate() method.
45
70
* @throws OpenemsException on error
46
71
*/
47
- @ Override
48
72
protected boolean activate (ComponentContext context , String id , String alias , boolean enabled , int unitId ,
49
- ConfigurationAdmin cm , String modbusReference , String modbusId , int readFromCommonBlockNo )
73
+ ConfigurationAdmin cm , String modbusReference , String modbusId , int readFromCommonBlockNo , Phase phase )
50
74
throws OpenemsException {
75
+ this .phase = phase ;
51
76
return super .activate (context , id , alias , enabled , unitId , cm , modbusReference , modbusId ,
52
77
readFromCommonBlockNo );
53
78
}
54
79
80
+ @ Override
81
+ protected boolean activate (ComponentContext context , String id , String alias , boolean enabled , int unitId ,
82
+ ConfigurationAdmin cm , String modbusReference , String modbusId , int readFromCommonBlockNo )
83
+ throws OpenemsException {
84
+ throw new IllegalArgumentException ("Use the other activate() method." );
85
+ }
86
+
55
87
/**
56
88
* Make sure to call this method from the inheriting OSGi Component.
57
89
*/
@@ -108,48 +140,122 @@ public String debugLog() {
108
140
protected void onSunSpecInitializationCompleted () {
109
141
this .logInfo (this .log , "SunSpec initialization finished. " + this .channels ().size () + " Channels available." );
110
142
111
- /*
112
- * SymmetricMeter
113
- */
143
+ this . channel ( SunSpecPvInverter . ChannelId . WRONG_PHASE_CONFIGURED )
144
+ . setNextValue ( this . isSinglePhase () ? this . phase == Phase . ALL : this . phase != Phase . ALL );
145
+
114
146
this .mapFirstPointToChannel (//
115
147
SymmetricMeter .ChannelId .FREQUENCY , //
116
148
ElementToChannelConverter .SCALE_FACTOR_3 , //
117
149
DefaultSunSpecModel .S111 .HZ , DefaultSunSpecModel .S112 .HZ , DefaultSunSpecModel .S113 .HZ ,
118
150
DefaultSunSpecModel .S101 .HZ , DefaultSunSpecModel .S102 .HZ , DefaultSunSpecModel .S103 .HZ );
151
+
119
152
this .mapFirstPointToChannel (//
120
153
SymmetricMeter .ChannelId .ACTIVE_POWER , //
121
154
ElementToChannelConverter .DIRECT_1_TO_1 , //
122
155
DefaultSunSpecModel .S111 .W , DefaultSunSpecModel .S112 .W , DefaultSunSpecModel .S113 .W ,
123
156
DefaultSunSpecModel .S101 .W , DefaultSunSpecModel .S102 .W , DefaultSunSpecModel .S103 .W );
157
+
124
158
this .mapFirstPointToChannel (//
125
159
SymmetricMeter .ChannelId .REACTIVE_POWER , //
126
160
ElementToChannelConverter .DIRECT_1_TO_1 , //
127
161
DefaultSunSpecModel .S111 .V_AR , DefaultSunSpecModel .S112 .V_AR , DefaultSunSpecModel .S113 .V_AR ,
128
162
DefaultSunSpecModel .S101 .V_AR , DefaultSunSpecModel .S102 .V_AR , DefaultSunSpecModel .S103 .V_AR );
163
+
129
164
this .mapFirstPointToChannel (//
130
165
SymmetricMeter .ChannelId .ACTIVE_PRODUCTION_ENERGY , //
131
166
ElementToChannelConverter .DIRECT_1_TO_1 , //
132
167
DefaultSunSpecModel .S111 .WH , DefaultSunSpecModel .S112 .WH , DefaultSunSpecModel .S113 .WH ,
133
168
DefaultSunSpecModel .S101 .WH , DefaultSunSpecModel .S102 .WH , DefaultSunSpecModel .S103 .WH );
169
+
134
170
this .mapFirstPointToChannel (//
135
- SymmetricMeter .ChannelId .VOLTAGE , //
136
- ElementToChannelConverter .SCALE_FACTOR_3 , //
137
- DefaultSunSpecModel .S111 .PH_VPH_A , DefaultSunSpecModel .S111 .PH_VPH_B , DefaultSunSpecModel .S111 .PH_VPH_C ,
138
- DefaultSunSpecModel .S112 .PH_VPH_A , DefaultSunSpecModel .S112 .PH_VPH_B , DefaultSunSpecModel .S112 .PH_VPH_C ,
139
- DefaultSunSpecModel .S113 .PH_VPH_A , DefaultSunSpecModel .S113 .PH_VPH_B , DefaultSunSpecModel .S113 .PH_VPH_C ,
140
- DefaultSunSpecModel .S101 .PH_VPH_A , DefaultSunSpecModel .S101 .PH_VPH_B , DefaultSunSpecModel .S101 .PH_VPH_C ,
141
- DefaultSunSpecModel .S102 .PH_VPH_A , DefaultSunSpecModel .S102 .PH_VPH_B , DefaultSunSpecModel .S102 .PH_VPH_C ,
142
- DefaultSunSpecModel .S103 .PH_VPH_A , DefaultSunSpecModel .S103 .PH_VPH_B ,
143
- DefaultSunSpecModel .S103 .PH_VPH_C );
171
+ ManagedSymmetricPvInverter .ChannelId .MAX_APPARENT_POWER , //
172
+ ElementToChannelConverter .DIRECT_1_TO_1 , //
173
+ DefaultSunSpecModel .S120 .W_RTG );
174
+
144
175
this .mapFirstPointToChannel (//
145
176
SymmetricMeter .ChannelId .CURRENT , //
146
177
ElementToChannelConverter .SCALE_FACTOR_3 , //
147
178
DefaultSunSpecModel .S111 .A , DefaultSunSpecModel .S112 .A , DefaultSunSpecModel .S113 .A ,
148
179
DefaultSunSpecModel .S101 .A , DefaultSunSpecModel .S102 .A , DefaultSunSpecModel .S103 .A );
180
+
181
+ /*
182
+ * SymmetricMeter
183
+ */
184
+ if (!this .isSinglePhase ) {
185
+ this .mapFirstPointToChannel (//
186
+ SymmetricMeter .ChannelId .VOLTAGE , //
187
+ ElementToChannelConverter .SCALE_FACTOR_3 , //
188
+ DefaultSunSpecModel .S112 .PH_VPH_A , DefaultSunSpecModel .S112 .PH_VPH_B ,
189
+ DefaultSunSpecModel .S112 .PH_VPH_C , //
190
+ DefaultSunSpecModel .S113 .PH_VPH_A , DefaultSunSpecModel .S113 .PH_VPH_B ,
191
+ DefaultSunSpecModel .S113 .PH_VPH_C , //
192
+ DefaultSunSpecModel .S102 .PH_VPH_A , DefaultSunSpecModel .S102 .PH_VPH_B ,
193
+ DefaultSunSpecModel .S102 .PH_VPH_C , //
194
+ DefaultSunSpecModel .S103 .PH_VPH_A , DefaultSunSpecModel .S103 .PH_VPH_B ,
195
+ DefaultSunSpecModel .S103 .PH_VPH_C );
196
+ return ;
197
+ }
198
+
199
+ /*
200
+ * AsymmetricMeter
201
+ */
202
+ switch (this .phase ) {
203
+ case ALL :
204
+ // use l1 when 'ALL' is configured and its not a tree phase inverter
205
+ case L1 :
206
+ this .mapFirstPointToChannel (AsymmetricMeter .ChannelId .VOLTAGE_L1 , //
207
+ ElementToChannelConverter .DIRECT_1_TO_1 , //
208
+ DefaultSunSpecModel .S101 .PH_VPH_A , DefaultSunSpecModel .S111 .PH_VPH_A );
209
+ break ;
210
+ case L2 :
211
+ this .mapFirstPointToChannel (AsymmetricMeter .ChannelId .VOLTAGE_L2 , //
212
+ ElementToChannelConverter .DIRECT_1_TO_1 , //
213
+ DefaultSunSpecModel .S101 .PH_VPH_B , DefaultSunSpecModel .S111 .PH_VPH_B );
214
+ break ;
215
+ case L3 :
216
+ this .mapFirstPointToChannel (AsymmetricMeter .ChannelId .VOLTAGE_L3 , //
217
+ ElementToChannelConverter .DIRECT_1_TO_1 , //
218
+ DefaultSunSpecModel .S101 .PH_VPH_C , DefaultSunSpecModel .S111 .PH_VPH_C );
219
+ break ;
220
+ }
221
+
149
222
this .mapFirstPointToChannel (//
150
- ManagedSymmetricPvInverter .ChannelId .MAX_APPARENT_POWER , //
223
+ SymmetricMeter .ChannelId .VOLTAGE , //
151
224
ElementToChannelConverter .DIRECT_1_TO_1 , //
152
- DefaultSunSpecModel .S120 .W_RTG );
225
+ DefaultSunSpecModel .S101 .PH_VPH_A , DefaultSunSpecModel .S111 .PH_VPH_A , //
226
+ DefaultSunSpecModel .S101 .PH_VPH_B , DefaultSunSpecModel .S111 .PH_VPH_B , //
227
+ DefaultSunSpecModel .S101 .PH_VPH_C , DefaultSunSpecModel .S111 .PH_VPH_C );
228
+
229
+ }
230
+
231
+ @ Override
232
+ protected void addBlock (int startAddress , SunSpecModel model , Priority priority ) throws OpenemsException {
233
+ super .addBlock (startAddress , model , priority );
234
+
235
+ if (Lists .newArrayList (DefaultSunSpecModel .S_101 , //
236
+ DefaultSunSpecModel .S_111 ) //
237
+ .stream () //
238
+ .anyMatch (t -> t .equals (model ))) {
239
+ // single phase
240
+ this .isSinglePhase = true ;
241
+ } else if (Lists .newArrayList (DefaultSunSpecModel .S_102 , //
242
+ DefaultSunSpecModel .S_112 ) //
243
+ .stream () //
244
+ .anyMatch (t -> t .equals (model ))) {
245
+ // split Phase
246
+ this .isSinglePhase = false ;
247
+ } else if (Lists .newArrayList (DefaultSunSpecModel .S_103 , //
248
+ DefaultSunSpecModel .S_113 ) //
249
+ .stream () //
250
+ .anyMatch (t -> t .equals (model ))) {
251
+ // three Phase
252
+ this .isSinglePhase = false ;
253
+ }
254
+
255
+ }
256
+
257
+ protected final boolean isSinglePhase () {
258
+ return this .isSinglePhase ;
153
259
}
154
260
155
261
@ Override
0 commit comments