View Javadoc
1   package net.sf.mbus4j.dataframes.datablocks;
2   
3   /*
4    * #%L
5    * mbus4j-core
6    * %%
7    * Copyright (C) 2009 - 2014 MBus4J
8    * %%
9    * mbus4j - Drivers for the M-Bus protocol - http://mbus4j.sourceforge.net/
10   * Copyright (C) 2009-2014, mbus4j.sf.net, and individual contributors as indicated
11   * by the @authors tag. See the copyright.txt in the distribution for a
12   * full listing of individual contributors.
13   * 
14   * This is free software; you can redistribute it and/or modify it
15   * under the terms of the GNU General Public License as
16   * published by the Free Software Foundation; either version 3 of
17   * the License, or (at your option) any later version.
18   * 
19   * This software is distributed in the hope that it will be useful,
20   * but WITHOUT ANY WARRANTY; without even the implied warranty of
21   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22   * Lesser General Public License for more details.
23   * 
24   * You should have received a copy of the GNU Lesser General Public
25   * License along with this software; if not, write to the Free
26   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
28   * #L%
29   */
30  import java.io.Serializable;
31  import java.util.Arrays;
32  import net.sf.json.JSONArray;
33  import net.sf.json.JSONObject;
34  
35  import net.sf.mbus4j.dataframes.datablocks.dif.DataFieldCode;
36  import net.sf.mbus4j.dataframes.datablocks.dif.FunctionField;
37  import net.sf.mbus4j.dataframes.datablocks.dif.VariableLengthType;
38  import net.sf.mbus4j.dataframes.datablocks.vif.ObjectAction;
39  import net.sf.mbus4j.dataframes.datablocks.vif.SiPrefix;
40  import net.sf.mbus4j.dataframes.datablocks.vif.UnitOfMeasurement;
41  import net.sf.mbus4j.dataframes.datablocks.vif.Vif;
42  import net.sf.mbus4j.dataframes.datablocks.vif.VifAscii;
43  import net.sf.mbus4j.dataframes.datablocks.vif.VifFB;
44  import net.sf.mbus4j.dataframes.datablocks.vif.VifFD;
45  import net.sf.mbus4j.dataframes.datablocks.vif.VifManufacturerSpecific;
46  import net.sf.mbus4j.dataframes.datablocks.vif.VifPrimary;
47  import net.sf.mbus4j.dataframes.datablocks.vif.VifTypes;
48  import net.sf.mbus4j.dataframes.datablocks.vif.Vife;
49  import net.sf.mbus4j.dataframes.datablocks.vif.VifeError;
50  import net.sf.mbus4j.dataframes.datablocks.vif.VifeFC;
51  import net.sf.mbus4j.dataframes.datablocks.vif.VifeManufacturerSpecific;
52  import net.sf.mbus4j.dataframes.datablocks.vif.VifeObjectAction;
53  import net.sf.mbus4j.dataframes.datablocks.vif.VifePrimary;
54  import net.sf.mbus4j.dataframes.datablocks.vif.VifeTypes;
55  import net.sf.mbus4j.json.JSONSerializable;
56  import net.sf.mbus4j.json.JsonSerializeType;
57  
58  /**
59   *
60   * @author arnep@users.sourceforge.net
61   * @version $Id: DataBlock.java 163 2016-10-07 18:53:55Z arnep $
62   */
63  public abstract class DataBlock implements Serializable, JSONSerializable, Cloneable {
64  
65      public final static Vife[] EMPTY_VIFE = new Vife[0];
66  
67      public static Class<? extends DataBlock> getDataBlockClass(Vif vif, Vife[] vifes, DataFieldCode dfc, VariableLengthType variableLengthType) {
68          if (vifes != null) {
69              for (Vife vife : vifes) {
70                  if (vife instanceof VifePrimary) {
71                      switch ((VifePrimary) vife) {
72                          case START_DATE_TIME_OF:
73                          case TIMESTAMP_OF_BEGIN_FIRST_LOWER:
74                          case TIMESTAMP_OF_END_FIRST_LOWER:
75                          case TIMESTAMP_BEGIN_LAST_LOWER:
76                          case TIMESTAMP_END_LAST_LOWER:
77                          case TIMESTAMP_BEGIN_FIRST_UPPER:
78                          case TIMESTAMP_END_FIRST_UPPER:
79                          case TIMESTAMP_BEGIN_LAST_UPPER:
80                          case TIMESTAMP_END_LAST_UPPER:
81                              return DateAndTimeDataBlock.class;
82                          case DURATION_OF_LIMIT_EXCEED_FIRST_LOWER_S:
83                              break;
84                          case DURATION_OF_LIMIT_EXCEED_FIRST_LOWER_MIN:
85                              break;
86                          case DURATION_OF_LIMIT_EXCEED_FIRST_LOWER_H:
87                              break;
88                          case DURATION_OF_LIMIT_EXCEED_FIRST_LOWER_D:
89                              break;
90                          case DURATION_OF_LIMIT_EXCEED_LAST_LOWER_S:
91                              break;
92                          case DURATION_OF_LIMIT_EXCEED_LAST_LOWER_MIN:
93                              break;
94                          case DURATION_OF_LIMIT_EXCEED_LAST_LOWER_H:
95                              break;
96                          case DURATION_OF_LIMIT_EXCEED_LAST_LOWER_D:
97                              break;
98                          case DURATION_OF_LIMIT_EXCEED_FIRST_UPPER_S:
99                              break;
100                         case DURATION_OF_LIMIT_EXCEED_FIRST_UPPER_MIN:
101                             break;
102                         case DURATION_OF_LIMIT_EXCEED_FIRST_UPPER_H:
103                             break;
104                         case DURATION_OF_LIMIT_EXCEED_FIRST_UPPER_D:
105                             break;
106                         case DURATION_OF_LIMIT_EXCEED_LAST_UPPER_S:
107                             break;
108                         case DURATION_OF_LIMIT_EXCEED_LAST_UPPER_MIN:
109                             break;
110                         case DURATION_OF_LIMIT_EXCEED_LAST_UPPER_H:
111                             break;
112                         case DURATION_OF_LIMIT_EXCEED_LAST_UPPER_D:
113                             break;
114                         case DURATION_OF_FIRST_S:
115                             break;
116                         case DURATION_OF_FIRST_MIN:
117                             break;
118                         case DURATION_OF_FIRST_H:
119                             break;
120                         case DURATION_OF_FIRST_D:
121                             break;
122                         case DURATION_OF_LAST_S:
123                             break;
124                         case DURATION_OF_LAST_MIN:
125                             break;
126                         case DURATION_OF_LAST_H:
127                             break;
128                         case DURATION_OF_LAST_D:
129                             break;
130                         case TIMESTAMP_BEGIN_OF_FIRST:
131                         case TIMESTAMP_END_OF_FIRST:
132                         case TIMESTAMP_BEGIN_OF_LAST:
133                         case TIMESTAMP_END_OF_LAST:
134                             return DateAndTimeDataBlock.class;
135 
136                         default:
137                     }
138                 } else {
139                     vifes = null;
140                 }
141             }
142         }
143 
144         switch (dfc) {
145             case NO_DATA:
146                 return EmptyDataBlock.class;
147             case SELECTION_FOR_READOUT:
148                 return ReadOutDataBlock.class;
149             case SPECIAL_FUNCTION_GLOBAL_READOUT_REQUEST:
150                 return ReadOutDataBlock.class;
151             case SPECIAL_FUNCTION_IDLE_FILLER:
152                 return EmptyDataBlock.class;
153             case SPECIAL_FUNCTION_MAN_SPEC_DATA_LAST_PACKET:
154                 return RawDataBlock.class;
155             case SPECIAL_FUNCTION_MAN_SPEC_DATA_PACKETS_FOLLOWS:
156                 return RawDataBlock.class;
157             case VARIABLE_LENGTH:
158                 switch (variableLengthType) {
159                     case STRING:
160                         return StringDataBlock.class;
161                     case BIG_DECIMAL:
162                         return BigDecimalDataBlock.class;
163                     default:
164                         return VariableLengthDataBlock.class;
165                 }
166             case _12_DIGIT_BCD:
167                 return LongDataBlock.class;
168             case _16_BIT_INTEGER:
169                 if (VifPrimary.TIMEPOINT_DATE.equals(vif)) {
170                     return DateDataBlock.class;
171                 } else {
172                     return ShortDataBlock.class;
173                 }
174             case _24_BIT_INTEGER:
175                 return IntegerDataBlock.class;
176             case _2_DIGIT_BCD:
177                 return ByteDataBlock.class;
178             case _32_BIT_INTEGER:
179                 if (VifPrimary.TIMEPOINT_TIME_AND_DATE.equals(vif)) {
180                     return DateAndTimeDataBlock.class;
181                 } else {
182                     return IntegerDataBlock.class;
183                 }
184             case _32_BIT_REAL:
185                 return RealDataBlock.class;
186             case _48_BIT_INTEGER:
187                 return LongDataBlock.class;
188             case _4_DIGIT_BCD:
189                 return ShortDataBlock.class;
190             case _64_BIT_INTEGER:
191                 if (VifPrimary.ENHANCED_IDENTIFICATION_RECORD.equals(vif)) {
192                     return EnhancedIdentificationDataBlock.class;
193                 } else {
194                     return LongDataBlock.class;
195                 }
196             case _6_DIGIT_BCD:
197                 return IntegerDataBlock.class;
198             case _8_BIT_INTEGER:
199                 return ByteDataBlock.class;
200             case _8_DIGIT_BCD:
201                 if (VifPrimary.ENHANCED_IDENTIFICATION_RECORD.equals(vif)) {
202                     return EnhancedIdentificationDataBlock.class;
203                 } else {
204                     return IntegerDataBlock.class;
205                 }
206             default:
207                 throw new UnsupportedOperationException("Unknown ControlCode");
208         }
209 
210     }
211 
212     public static Vif getVif(String vifTypeLabel, String vifLabel, UnitOfMeasurement unitOfMeasurement, SiPrefix siPrefix, Integer exponent) {
213         VifTypes vifType = VifTypes.fromLabel(vifTypeLabel);
214         switch (vifType) {
215             case PRIMARY:
216                 return VifPrimary.assemble(vifLabel, unitOfMeasurement, siPrefix, exponent);
217             case FB_EXTENTION:
218                 return VifFB.assemble(vifLabel, unitOfMeasurement, siPrefix, exponent);
219             case FD_EXTENTION:
220                 return VifFD.assemble(vifLabel, unitOfMeasurement, siPrefix, exponent);
221             case ASCII:
222                 return new VifAscii(vifLabel);
223             case MANUFACTURER_SPECIFIC:
224                 return new VifManufacturerSpecific();
225             default: {
226                 throw new IllegalArgumentException("Unknown vifType: " + vifTypeLabel);
227             }
228         }
229     }
230 
231     public static Vif vifFromJSON(JSONObject jsonVif) {
232         return getVif(jsonVif.getString("vifType"),
233                 jsonVif.containsKey("description") ? jsonVif.getString("description") : null,
234                 jsonVif.containsKey("unitOfMeasurement") ? UnitOfMeasurement.fromLabel(jsonVif.getString("unitOfMeasurement")) : null,
235                 jsonVif.containsKey("siPrefix") ? SiPrefix.fromLabel(jsonVif.getString("siPrefix")) : null,
236                 jsonVif.containsKey("exponent") ? jsonVif.getInt("exponent") : null);
237     }
238 
239     public static Vife getVife(String vifeTypeLabel, String vifeLabel) {
240         VifeTypes vifeType = VifeTypes.fromLabel(vifeTypeLabel);
241         switch (vifeType) {
242             case PRIMARY:
243                 return VifePrimary.fromLabel(vifeLabel);
244             case FC_EXTENSION:
245                 return VifeFC.fromLabel(vifeLabel);
246             case ERROR:
247                 return VifeError.fromLabel(vifeLabel);
248             case OBJECT_ACTION:
249                 return VifeObjectAction.fromLabel(vifeLabel);
250             case MAN_SPEC:
251                 return new VifeManufacturerSpecific((byte) Integer.parseInt(vifeLabel, 16));
252             default:
253                 throw new IllegalArgumentException(vifeType.getLabel());
254         }
255     }
256 
257     public static Vife[] vifesFromJSON(VifTypes vifType, JSONArray jsonVifes) {
258         if (jsonVifes instanceof JSONArray) {
259             Vife[] result = new Vife[jsonVifes.size()];
260             for (int i = 0; i < jsonVifes.size(); i++) {
261                 result[i] = getVife(jsonVifes.getJSONObject(i).getString("vifeType"), jsonVifes.getJSONObject(i).getString("name"));
262             }
263             return result;
264         } else {
265             return new Vife[0];
266         }
267     }
268     private ObjectAction action;
269     private Vif vif;
270     /**
271      * Extend paramdescription by VIFE value from table 8.4.5
272      *
273      * @param vife
274      */
275     private Vife[] vifes = EMPTY_VIFE;
276     private FunctionField functionField;
277     private long storageNumber;
278     private int tariff;
279     private DataFieldCode dataFieldCode;
280     private short subUnit;
281 
282     public DataBlock() {
283         super();
284     }
285 
286     @Deprecated
287     public DataBlock(DataFieldCode dif, FunctionField functionField, short subUnit, int tariff, long storageNumber, Vif vif, Vife... vifes) {
288         super();
289         this.dataFieldCode = dif;
290         this.functionField = functionField;
291         this.subUnit = subUnit;
292         this.tariff = tariff;
293         this.storageNumber = storageNumber;
294         this.vif = vif;
295         if (vifes.length > 0) {
296             this.vifes = Arrays.copyOf(vifes, vifes.length);
297         }
298     }
299 
300     @Deprecated
301     public DataBlock(DataFieldCode dif, Vif vif, Vife... vifes) {
302         super();
303         this.dataFieldCode = dif;
304         functionField = FunctionField.INSTANTANEOUS_VALUE;
305         this.vif = vif;
306         if (vifes.length > 0) {
307             this.vifes = Arrays.copyOf(vifes, vifes.length);
308         }
309     }
310 
311     public boolean addVife(Vife vife) {
312         vifes = Arrays.copyOf(vifes, vifes.length + 1);
313         vifes[vifes.length - 1] = vife;
314         return true;
315     }
316 
317     /**
318      * @return the action
319      */
320     public ObjectAction getAction() {
321         return action;
322     }
323 
324     public DataFieldCode getDataFieldCode() {
325         return dataFieldCode;
326     }
327 
328     public Integer getExponent() {
329         return vif != null ? vif.getExponent() : null;
330     }
331 
332     public FunctionField getFunctionField() {
333         return functionField;
334     }
335 
336     public String getParamDescr() {
337         StringBuilder sb = new StringBuilder();
338         if (vif != null) {
339             sb.append(vif.getLabel());
340         }
341         if (vifes != null) {
342             for (Vife vife : vifes) {
343                 sb.append(", ").append(vife.getLabel());
344             }
345         }
346         return sb.toString();
347     }
348 
349     public SiPrefix getSiPrefix() {
350         return vif == null ? null : vif.getSiPrefix();
351     }
352 
353     public long getStorageNumber() {
354         return storageNumber;
355     }
356 
357     /**
358      * @return the deviceUnit
359      */
360     public short getSubUnit() {
361         return subUnit;
362     }
363 
364     public int getTariff() {
365         return tariff;
366     }
367 
368     public UnitOfMeasurement getUnitOfMeasurement() {
369         return vif != null ? vif.getUnitOfMeasurement() : null;
370     }
371 
372     public abstract String getValueAsString();
373 
374     public Vif getVif() {
375         return vif;
376     }
377 
378     public Vife[] getVifes() {
379         return vifes;
380     }
381 
382     /**
383      * @param action the action to set
384      */
385     public void setAction(ObjectAction action) {
386         this.action = action;
387     }
388 
389     public void setFunctionField(FunctionField functionField) {
390         this.functionField = functionField;
391     }
392 
393     public void setStorageNumber(long storageNumber) {
394         this.storageNumber = storageNumber;
395     }
396 
397     /**
398      * @param subUnit the subUnit to set
399      */
400     public void setSubUnit(short subUnit) {
401         this.subUnit = subUnit;
402     }
403 
404     public void setTariff(int tariff) {
405         this.tariff = tariff;
406     }
407 
408     public void setVif(Vif vif) {
409         this.vif = vif;
410     }
411 
412     public void toString(StringBuilder sb, String inset) {
413         if (action != null) {
414             sb.append(inset).append("action = ").append(getAction()).append("\n");
415         }
416         sb.append(inset).append("dataType = ").append(getDataFieldCode()).append("\n");
417         if (vif != null) {
418             sb.append(inset).append("description = ").append(getParamDescr()).append("\n");
419             if (getDataFieldCode().equals(DataFieldCode.NO_DATA) || getDataFieldCode().equals(DataFieldCode.SELECTION_FOR_READOUT)) {
420                 sb.append(inset).append("unit =");
421             } else {
422                 sb.append(inset).append("value = ").append(getValueAsString());
423             }
424             if (getUnitOfMeasurement() != null) {
425                 if (getExponent() != null) {
426 
427                     if (getExponent() > 0) {
428                         sb.append(" * 1");
429                         for (int i = 0; i < getExponent(); i++) {
430                             sb.append("0");
431                         }
432                     } else if (getExponent() < 0) {
433                         sb.append(" * 0.");
434                         for (int i = -1; i > getExponent(); i--) {
435                             sb.append("0");
436                         }
437                         sb.append("1");
438 
439                     }
440                 }
441   
442                 final Double cf = VifePrimary.getVifeCorrectionFactor(vifes);
443                 if (!Double.isNaN(cf)) {
444                     sb.append(" * ");
445                     sb.append(cf);
446                 }
447 
448                 sb.append(" [");
449                 if (getSiPrefix() != null) {
450                     sb.append(getSiPrefix());
451                 }
452                 sb.append(getUnitOfMeasurement()).append("]\n");
453             } else {
454                 sb.append("\n");
455             }
456         }
457         if (functionField != null) {
458             sb.append(inset).append("tariff = ").append(getTariff()).append('\n');
459             sb.append(inset).append("storageNumber = ").append(getStorageNumber()).append("\n");
460             sb.append(inset).append("functionField = ").append(getFunctionField()).append("\n");
461             sb.append(inset).append("subUnit = ").append(getSubUnit()).append("\n");
462         }
463     }
464 
465     @Override
466     public String toString() {
467         StringBuilder sb = new StringBuilder();
468         toString(sb, "");
469         return sb.toString();
470     }
471 
472     protected void accumulateDatatoJSON(JSONObject json) {
473     }
474 
475     @Override
476     public JSONObject toJSON(JsonSerializeType jsonSerializeType) {
477         JSONObject result = new JSONObject();
478         JSONObject drh = new JSONObject();
479         JSONObject dib = new JSONObject();
480         JSONObject vib = new JSONObject();
481 
482         if (getAction() != null) {
483             dib.accumulate("action", getAction().getLabel());
484         }
485         dib.accumulate("dataFieldCode", getDataFieldCode().getLabel());
486         if (DataFieldCode.VARIABLE_LENGTH.equals(getDataFieldCode())) {
487             if (this instanceof StringDataBlock) {
488                 dib.accumulate("variableLengthType", VariableLengthType.STRING.getLabel());
489             } else {
490                 throw new UnsupportedOperationException("Varoable datablock:" + this.getClass().getName());
491             }
492         }
493         if (DataFieldCode.SPECIAL_FUNCTION_MAN_SPEC_DATA_LAST_PACKET.equals(dataFieldCode) || DataFieldCode.SPECIAL_FUNCTION_MAN_SPEC_DATA_PACKETS_FOLLOWS.equals(dataFieldCode) || DataFieldCode.SPECIAL_FUNCTION_GLOBAL_READOUT_REQUEST.equals(dataFieldCode)) {
494             drh.accumulate("dib", dib);
495         } else {
496             dib.accumulate("functionField", getFunctionField().getLabel());
497             dib.accumulate("storageNumber", getStorageNumber());
498             dib.accumulate("subUnit", getSubUnit());
499             dib.accumulate("tariff", getTariff());
500             drh.accumulate("dib", dib);
501             JSONObject jsonVif = new JSONObject();
502             jsonVif.accumulate("vifType", vif.getVifType().getLabel());
503             jsonVif.accumulate("description", vif.getLabel());
504             if (vif.getUnitOfMeasurement() != null) {
505                 jsonVif.accumulate("unitOfMeasurement", vif.getUnitOfMeasurement().getLabel());
506             }
507             if (vif.getSiPrefix() != null) {
508                 jsonVif.accumulate("siPrefix", vif.getSiPrefix().getLabel());
509             }
510             if (vif.getExponent() != null) {
511                 jsonVif.accumulate("exponent", vif.getExponent());
512             }
513 
514             vib.accumulate("vif", jsonVif);
515             if (getVifes().length > 0) {
516                 JSONArray jsonVifes = new JSONArray();
517                 for (Vife value : vifes) {
518                     JSONObject jsonVife = new JSONObject();
519                     jsonVife.accumulate("vifeType", value.getVifeType().getLabel());
520                     jsonVife.accumulate("name", value.getLabel());
521                     jsonVifes.add(jsonVife);
522                 }
523                 vib.accumulate("vifes", jsonVifes);
524             }
525             drh.accumulate("vib", vib);
526         }
527         result.accumulate("drh", drh);
528         if ((JsonSerializeType.ALL == jsonSerializeType) || (JsonSerializeType.SLAVE_CONFIG == jsonSerializeType)) {
529             accumulateDatatoJSON(result);
530         }
531         return result;
532     }
533 
534     @Override
535     public void fromJSON(JSONObject json) {
536         JSONObject drh = json.getJSONObject("drh");
537         JSONObject dib = drh.getJSONObject("dib");
538         JSONObject vib = drh.getJSONObject("vib");
539         if (dib.get("action") != null) {
540             setAction(ObjectAction.fromLabel(dib.getString("action")));
541         }
542 
543         setDataFieldCode(DataFieldCode.fromLabel(dib.getString("dataFieldCode")));
544         if ((DataFieldCode.SPECIAL_FUNCTION_MAN_SPEC_DATA_LAST_PACKET == dataFieldCode)
545                 || (DataFieldCode.SPECIAL_FUNCTION_MAN_SPEC_DATA_PACKETS_FOLLOWS == dataFieldCode)
546                 || (DataFieldCode.SPECIAL_FUNCTION_GLOBAL_READOUT_REQUEST == dataFieldCode)) {
547         } else {
548             setFunctionField(FunctionField.fromLabel(dib.getString("functionField")));
549             setStorageNumber(dib.getLong("storageNumber"));
550             setSubUnit((short) dib.getInt("subUnit"));
551             setTariff(dib.getInt("tariff"));
552             if (!vib.isNullObject()) {
553                 JSONObject jsonVif = vib.getJSONObject("vif");
554                 vif = vifFromJSON(jsonVif);
555                 if (vib.containsKey("vifes")) {
556 
557                     Vife[] parsedVifes = vifesFromJSON(vif.getVifType(), vib.getJSONArray("vifes"));
558                     if (parsedVifes.length > 0) {
559                         this.vifes = Arrays.copyOf(parsedVifes, parsedVifes.length);
560                     } else {
561                         vifes = EMPTY_VIFE;
562                     }
563                 } else {
564                     vifes = EMPTY_VIFE;
565                 }
566             }
567         }
568     }
569 
570     public abstract void setValue(String text);
571 
572     /**
573      * @param dataFieldCode the dataFieldCode to set
574      */
575     public void setDataFieldCode(DataFieldCode dataFieldCode) {
576         this.dataFieldCode = dataFieldCode;
577     }
578 
579     public void clearVifes() {
580         vifes = EMPTY_VIFE;
581     }
582 
583     /* Helper method */
584     protected String formatBcdError(String bcdError) {
585         if (bcdError == null) {
586             return bcdError;
587         }
588         int length;
589         switch (getDataFieldCode()) {
590             case _2_DIGIT_BCD:
591                 length = 2;
592                 break;
593             case _4_DIGIT_BCD:
594                 length = 4;
595                 break;
596             case _6_DIGIT_BCD:
597                 length = 6;
598                 break;
599             case _8_DIGIT_BCD:
600                 length = 8;
601                 break;
602             case _12_DIGIT_BCD:
603                 length = 12;
604                 break;
605             default:
606                 throw new RuntimeException("Unknown DFC " + getDataFieldCode());
607         }
608 
609         if (bcdError.length() == length) {
610             return bcdError;
611         } else if ((bcdError.length() < length) && (bcdError.length() > 0)) {
612             StringBuilder sb = new StringBuilder(length);
613             for (int i = 0; i < length - bcdError.length(); i++) {
614                 sb.append(' ');
615             }
616             sb.append(bcdError);
617             return sb.toString();
618         } else {
619             throw new RuntimeException("Bcd is too long: \"" + bcdError + "\"");
620         }
621 
622     }
623 
624     
625     public int getCorrectionExponent(SiPrefix siPrefix) {
626             return  getSiPrefix().getExponent()- siPrefix.getExponent() + getExponent() + VifePrimary.getVifeCorrectionExponent(vifes);
627     }
628 
629     public double getCorrectionConstant() {
630          return VifePrimary.getVifeCorrectionConstant(vifes);
631     }
632     
633 }