2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.teleinfo.internal.reader.io;
15 import java.io.BufferedReader;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.InputStreamReader;
19 import java.nio.charset.StandardCharsets;
20 import java.time.LocalDate;
21 import java.util.HashMap;
23 import java.util.UUID;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.Future;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.TimeoutException;
32 import org.eclipse.jdt.annotation.NonNullByDefault;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.openhab.binding.teleinfo.internal.dto.Frame;
35 import org.openhab.binding.teleinfo.internal.dto.cbemm.FrameCbemm;
36 import org.openhab.binding.teleinfo.internal.dto.cbemm.FrameCbemmBaseOption;
37 import org.openhab.binding.teleinfo.internal.dto.cbemm.FrameCbemmEjpOption;
38 import org.openhab.binding.teleinfo.internal.dto.cbemm.FrameCbemmHcOption;
39 import org.openhab.binding.teleinfo.internal.dto.cbemm.FrameCbemmTempoOption;
40 import org.openhab.binding.teleinfo.internal.dto.cbemm.evoicc.FrameCbemmEvolutionIcc;
41 import org.openhab.binding.teleinfo.internal.dto.cbemm.evoicc.FrameCbemmEvolutionIccBaseOption;
42 import org.openhab.binding.teleinfo.internal.dto.cbemm.evoicc.FrameCbemmEvolutionIccEjpOption;
43 import org.openhab.binding.teleinfo.internal.dto.cbemm.evoicc.FrameCbemmEvolutionIccHcOption;
44 import org.openhab.binding.teleinfo.internal.dto.cbemm.evoicc.FrameCbemmEvolutionIccTempoOption;
45 import org.openhab.binding.teleinfo.internal.dto.cbetm.FrameCbetmLong;
46 import org.openhab.binding.teleinfo.internal.dto.cbetm.FrameCbetmLongBaseOption;
47 import org.openhab.binding.teleinfo.internal.dto.cbetm.FrameCbetmLongEjpOption;
48 import org.openhab.binding.teleinfo.internal.dto.cbetm.FrameCbetmLongHcOption;
49 import org.openhab.binding.teleinfo.internal.dto.cbetm.FrameCbetmLongTempoOption;
50 import org.openhab.binding.teleinfo.internal.dto.cbetm.FrameCbetmShort;
51 import org.openhab.binding.teleinfo.internal.dto.common.FrameBaseOption;
52 import org.openhab.binding.teleinfo.internal.dto.common.FrameEjpOption;
53 import org.openhab.binding.teleinfo.internal.dto.common.FrameHcOption;
54 import org.openhab.binding.teleinfo.internal.dto.common.FrameTempoOption;
55 import org.openhab.binding.teleinfo.internal.dto.common.FrameTempoOption.CouleurDemain;
56 import org.openhab.binding.teleinfo.internal.dto.common.FrameTempoOption.ProgrammeCircuit1;
57 import org.openhab.binding.teleinfo.internal.dto.common.FrameTempoOption.ProgrammeCircuit2;
58 import org.openhab.binding.teleinfo.internal.dto.common.Hhphc;
59 import org.openhab.binding.teleinfo.internal.dto.common.Ptec;
60 import org.openhab.binding.teleinfo.internal.reader.io.serialport.ConversionException;
61 import org.openhab.binding.teleinfo.internal.reader.io.serialport.FrameUtil;
62 import org.openhab.binding.teleinfo.internal.reader.io.serialport.InvalidFrameException;
63 import org.openhab.binding.teleinfo.internal.reader.io.serialport.Label;
64 import org.openhab.binding.teleinfo.internal.reader.io.serialport.converter.Converter;
65 import org.openhab.binding.teleinfo.internal.reader.io.serialport.converter.CouleurDemainConverter;
66 import org.openhab.binding.teleinfo.internal.reader.io.serialport.converter.FloatConverter;
67 import org.openhab.binding.teleinfo.internal.reader.io.serialport.converter.HhphcConverter;
68 import org.openhab.binding.teleinfo.internal.reader.io.serialport.converter.IntegerConverter;
69 import org.openhab.binding.teleinfo.internal.reader.io.serialport.converter.PtecConverter;
70 import org.openhab.binding.teleinfo.internal.reader.io.serialport.converter.StringConverter;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
75 * InputStream for Teleinfo {@link Frame} in serial port format.
78 * The {@link TeleinfoInputStream} class is an {@link InputStream} to decode/read Teleinfo frames.
80 * @author Nicolas SIBERIL - Initial contribution
83 public class TeleinfoInputStream extends InputStream {
85 public static final long DEFAULT_TIMEOUT_NEXT_HEADER_FRAME_US = 33400;
86 public static final long DEFAULT_TIMEOUT_READING_FRAME_US = 33400;
88 private final Logger logger = LoggerFactory.getLogger(TeleinfoInputStream.class);
89 private static final Map<Class<?>, Converter> LABEL_VALUE_CONVERTERS;
91 private BufferedReader bufferedReader;
92 private @Nullable String groupLine;
93 private ExecutorService executorService;
94 private long waitNextHeaderFrameTimeoutInUs;
95 private long readingFrameTimeoutInUs;
96 private boolean autoRepairInvalidADPSgroupLine;
97 private boolean useOwnScheduler;
100 LABEL_VALUE_CONVERTERS = new HashMap<>();
101 LABEL_VALUE_CONVERTERS.put(Integer.class, new IntegerConverter());
102 LABEL_VALUE_CONVERTERS.put(String.class, new StringConverter());
103 LABEL_VALUE_CONVERTERS.put(Float.class, new FloatConverter());
104 LABEL_VALUE_CONVERTERS.put(Ptec.class, new PtecConverter());
105 LABEL_VALUE_CONVERTERS.put(Hhphc.class, new HhphcConverter());
106 LABEL_VALUE_CONVERTERS.put(CouleurDemain.class, new CouleurDemainConverter());
109 public TeleinfoInputStream(final InputStream teleinfoInputStream) {
110 this(teleinfoInputStream, DEFAULT_TIMEOUT_NEXT_HEADER_FRAME_US, DEFAULT_TIMEOUT_READING_FRAME_US, false);
113 public TeleinfoInputStream(final InputStream teleinfoInputStream, boolean autoRepairInvalidADPSgroupLine) {
114 this(teleinfoInputStream, DEFAULT_TIMEOUT_NEXT_HEADER_FRAME_US, DEFAULT_TIMEOUT_READING_FRAME_US,
115 autoRepairInvalidADPSgroupLine);
118 public TeleinfoInputStream(final @Nullable InputStream teleinfoInputStream, long waitNextHeaderFrameTimeoutInUs,
119 long readingFrameTimeoutInUs, boolean autoRepairInvalidADPSgroupLine) {
120 this(teleinfoInputStream, waitNextHeaderFrameTimeoutInUs, readingFrameTimeoutInUs,
121 autoRepairInvalidADPSgroupLine, null);
124 public TeleinfoInputStream(final @Nullable InputStream teleinfoInputStream, long waitNextHeaderFrameTimeoutInUs,
125 long readingFrameTimeoutInUs, boolean autoRepairInvalidADPSgroupLine,
126 @Nullable ExecutorService executorService) {
127 if (teleinfoInputStream == null) {
128 throw new IllegalArgumentException("Teleinfo inputStream is null");
131 this.waitNextHeaderFrameTimeoutInUs = waitNextHeaderFrameTimeoutInUs;
132 this.readingFrameTimeoutInUs = readingFrameTimeoutInUs;
133 this.autoRepairInvalidADPSgroupLine = autoRepairInvalidADPSgroupLine;
134 if (executorService == null) {
135 this.executorService = Executors.newFixedThreadPool(2);
136 this.useOwnScheduler = true;
138 this.executorService = executorService;
139 this.useOwnScheduler = false;
142 this.bufferedReader = new BufferedReader(new InputStreamReader(teleinfoInputStream, StandardCharsets.US_ASCII));
148 public void close() throws IOException {
149 logger.debug("close() [start]");
150 bufferedReader.close();
151 if (useOwnScheduler) {
152 executorService.shutdownNow();
155 logger.debug("close() [end]");
159 * Returns the next frame.
161 * @return the next frame or null if end of stream
162 * @throws TimeoutException
163 * @throws IOException
164 * @throws InvalidFrameException
167 public synchronized @Nullable Frame readNextFrame() throws TimeoutException, InvalidFrameException, IOException {
168 // seek the next header frame
169 Future<@Nullable Void> seekNextHeaderFrameTask = executorService.submit(() -> {
170 while (!isHeaderFrame(groupLine)) {
171 groupLine = bufferedReader.readLine();
172 if (logger.isTraceEnabled()) {
173 logger.trace("groupLine = {}", groupLine);
175 if (groupLine == null) { // end of stream
176 logger.trace("end of stream reached !");
181 logger.trace("header frame found !");
185 logger.debug("seeking the next header frame...");
186 logger.trace("waitNextHeaderFrameTimeoutInUs = {}", waitNextHeaderFrameTimeoutInUs);
187 seekNextHeaderFrameTask.get(waitNextHeaderFrameTimeoutInUs, TimeUnit.MICROSECONDS);
189 if (groupLine == null) { // end of stream
192 } catch (InterruptedException e) {
193 logger.debug("Got interrupted exception", e);
194 Thread.currentThread().interrupt();
195 } catch (ExecutionException e) {
196 rethrowTaskExecutionException(e);
199 Future<Map<Label, Object>> nextFrameFuture = executorService.submit(new Callable<Map<Label, Object>>() {
201 public Map<Label, Object> call() throws Exception {
203 Map<Label, Object> frameValues = new HashMap<>();
204 while ((groupLine = bufferedReader.readLine()) != null && !isHeaderFrame(groupLine)) {
205 logger.trace("groupLine = {}", groupLine);
206 String groupLineRef = groupLine;
207 if (groupLineRef != null) {
208 String[] groupLineTokens = groupLineRef.split("\\s");
209 if (groupLineTokens.length != 2 && groupLineTokens.length != 3) {
210 final String error = String.format("The groupLine '%1$s' is incomplete", groupLineRef);
211 throw new InvalidFrameException(error);
213 String labelStr = groupLineTokens[0];
214 String valueString = groupLineTokens[1];
216 // verify integrity (through checksum)
217 char checksum = (groupLineTokens.length == 3 ? groupLineTokens[2].charAt(0) : ' ');
218 char computedChecksum = FrameUtil.computeGroupLineChecksum(labelStr, valueString);
219 if (computedChecksum != checksum) {
220 logger.trace("computedChecksum = {}", computedChecksum);
221 logger.trace("checksum = {}", checksum);
222 final String error = String.format(
223 "The groupLine '%s' is corrupted (integrity not checked). Actual checksum: '%s' / Expected checksum: '%s'",
224 groupLineRef, checksum, computedChecksum);
225 throw new InvalidFrameException(error);
230 label = Label.valueOf(labelStr);
231 } catch (IllegalArgumentException e) {
232 if (autoRepairInvalidADPSgroupLine && labelStr.startsWith(Label.ADPS.name())) {
233 // in this hardware issue, label variable is composed by label name and value. E.g:
235 logger.warn("Try to auto repair malformed ADPS groupLine '{}'", labelStr);
237 valueString = labelStr.substring(Label.ADPS.name().length());
239 final String error = String.format("The label '%s' is unknown", labelStr);
240 throw new InvalidFrameException(error);
244 Class<?> labelType = label.getType();
245 Converter converter = LABEL_VALUE_CONVERTERS.get(labelType);
247 Object value = converter.convert(valueString);
249 frameValues.put(label, value);
251 } catch (ConversionException e) {
252 final String error = String.format("An error occurred during '%s' value conversion",
254 throw new InvalidFrameException(error, e);
264 logger.debug("reading data frame...");
265 logger.trace("readingFrameTimeoutInUs = {}", readingFrameTimeoutInUs);
266 Map<Label, Object> frameValues = nextFrameFuture.get(readingFrameTimeoutInUs, TimeUnit.MICROSECONDS);
268 // build the frame from map values
269 final Frame frame = buildFrame(frameValues);
270 frame.setTimestamp(LocalDate.now());
271 frame.setId(UUID.randomUUID());
274 } catch (InterruptedException e) {
275 logger.debug("Got interrupted exception", e);
276 Thread.currentThread().interrupt();
277 throw new IllegalStateException(e);
278 } catch (ExecutionException e) {
279 rethrowTaskExecutionException(e);
284 public long getWaitNextHeaderFrameTimeoutInUs() {
285 return waitNextHeaderFrameTimeoutInUs;
288 public void setWaitNextHeaderFrameTimeoutInUs(long waitNextHeaderFrameTimeoutInUs) {
289 this.waitNextHeaderFrameTimeoutInUs = waitNextHeaderFrameTimeoutInUs;
292 public long getReadingFrameTimeoutInUs() {
293 return readingFrameTimeoutInUs;
296 public void setReadingFrameTimeoutInUs(long readingFrameTimeoutInUs) {
297 this.readingFrameTimeoutInUs = readingFrameTimeoutInUs;
300 public boolean isAutoRepairInvalidADPSgroupLine() {
301 return autoRepairInvalidADPSgroupLine;
304 public void setAutoRepairInvalidADPSgroupLine(boolean autoRepairInvalidADPSgroupLine) {
305 this.autoRepairInvalidADPSgroupLine = autoRepairInvalidADPSgroupLine;
309 public int read() throws IOException {
310 throw new UnsupportedOperationException("The 'read()' is not supported");
313 private boolean isHeaderFrame(final @Nullable String line) {
314 // A new teleinfo trame begin with '3' and '2' bytes (END OF TEXT et START OF TEXT)
315 return (line != null && line.length() > 1 && line.codePointAt(0) == 3 && line.codePointAt(1) == 2);
318 private Frame buildFrame(final Map<Label, Object> frameValues) throws InvalidFrameException {
319 if (frameValues.containsKey(Label.IINST1)) {
320 if (frameValues.containsKey(Label.IMAX1)) {
321 return buildFrameCbetmLong(frameValues);
323 return buildFrameCbetmShort(frameValues);
325 } else if (frameValues.containsKey(Label.PAPP)) {
326 return buildFrameCbemmEvolutionIcc(frameValues);
328 return buildFrameCbemm(frameValues);
332 private FrameCbetmLong buildFrameCbetmLong(final Map<Label, Object> frameValues) throws InvalidFrameException {
333 logger.trace("buildFrameCbetmLong(Map<Label, Object>) [start]");
334 final FrameCbetmLong frameCbetm;
335 String optionTarif = getRequiredLabelValue(Label.OPTARIF, frameValues);
336 if ("BASE".equals(optionTarif)) {
337 frameCbetm = buildFrameCbetmLongBaseOption(frameValues);
338 } else if ("HC..".equals(optionTarif)) {
339 frameCbetm = buildFrameCbetmLongHcOption(frameValues);
340 } else if ("EJP.".equals(optionTarif)) {
341 frameCbetm = buildFrameCbetmLongEjpOption(frameValues);
342 } else if (optionTarif.startsWith("BBR") && optionTarif.length() == 4) {
343 ProgrammeCircuit1 prgCircuit1 = convertProgrammeCircuit1(optionTarif.charAt(3));
344 ProgrammeCircuit2 prgCircuit2 = convertProgrammeCircuit2(optionTarif.charAt(3));
345 frameCbetm = buildFrameCbetmLongTempoOption(frameValues, prgCircuit1, prgCircuit2);
347 final String error = String.format("The option Tarif '%s' is not supported", optionTarif);
348 throw new InvalidFrameException(error);
350 logger.trace("buildFrameCbetmLong(Map<Label, Object>) [end]");
354 private void setCbetmCommonFrameFields(final FrameCbetmLong frame, final Map<Label, Object> frameValues)
355 throws InvalidFrameException {
356 logger.trace("setCbetmCommonFrameFields(Frame, Map<Label, Object>) [start]");
357 frame.setAdco(getRequiredLabelValue(Label.ADCO, frameValues));
358 frame.setIsousc(getRequiredLabelValue(Label.ISOUSC, frameValues));
359 frame.setIinst1(getRequiredLabelValue(Label.IINST1, frameValues));
360 frame.setIinst2(getRequiredLabelValue(Label.IINST2, frameValues));
361 frame.setIinst3(getRequiredLabelValue(Label.IINST3, frameValues));
362 frame.setImax1(getRequiredLabelValue(Label.IMAX1, frameValues));
363 frame.setImax2(getRequiredLabelValue(Label.IMAX2, frameValues));
364 frame.setImax3(getRequiredLabelValue(Label.IMAX3, frameValues));
365 frame.setPtec(getRequiredLabelValue(Label.PTEC, frameValues));
366 frame.setPmax(getRequiredLabelValue(Label.PMAX, frameValues));
367 frame.setPapp(getRequiredLabelValue(Label.PAPP, frameValues));
368 frame.setMotdetat(getRequiredLabelValue(Label.MOTDETAT, frameValues));
369 frame.setPpot(getRequiredLabelValue(Label.PPOT, frameValues));
370 logger.trace("setCbetmCommonFrameFields(Frame, Map<Label, Object>) [end]");
373 private FrameCbetmLongBaseOption buildFrameCbetmLongBaseOption(final Map<Label, Object> frameValues)
374 throws InvalidFrameException {
375 logger.trace("buildFrameCbetmBaseOption(Map<Label, Object>) [start]");
376 FrameCbetmLongBaseOption frame = new FrameCbetmLongBaseOption();
377 setCbetmCommonFrameFields(frame, frameValues);
378 setFrameBaseOptionFields(frame, frameValues);
379 logger.trace("buildFrameCbetmBaseOption(Map<Label, Object>) [end]");
383 private FrameCbetmLongHcOption buildFrameCbetmLongHcOption(final Map<Label, Object> frameValues)
384 throws InvalidFrameException {
385 logger.trace("buildFrameCbetmHcOption(Map<Label, Object>) [start]");
386 FrameCbetmLongHcOption frame = new FrameCbetmLongHcOption();
387 setCbetmCommonFrameFields(frame, frameValues);
388 setFrameHcOptionFields(frame, frameValues);
389 logger.trace("buildFrameCbetmHcOption(Map<Label, Object>) [end]");
393 private FrameCbetmLongEjpOption buildFrameCbetmLongEjpOption(final Map<Label, Object> frameValues)
394 throws InvalidFrameException {
395 logger.trace("buildFrameCbetmEjpOption(Map<Label, Object>) [start]");
396 FrameCbetmLongEjpOption frame = new FrameCbetmLongEjpOption();
397 setCbetmCommonFrameFields(frame, frameValues);
398 setFrameEjpOptionFields(frame, frameValues);
399 logger.trace("buildFrameCbetmEjpOption(Map<Label, Object>) [end]");
403 private FrameCbetmLongTempoOption buildFrameCbetmLongTempoOption(final Map<Label, Object> frameValues,
404 ProgrammeCircuit1 prgCircuit1, ProgrammeCircuit2 prgCircuit2) throws InvalidFrameException {
405 logger.trace("buildFrameCbetmTempoOption(Map<Label, Object>) [start]");
406 FrameCbetmLongTempoOption frame = new FrameCbetmLongTempoOption();
407 setCbetmCommonFrameFields(frame, frameValues);
408 setFrameTempoOptionFields(frame, frameValues, prgCircuit1, prgCircuit2);
409 logger.trace("buildFrameCbetmTempoOption(Map<Label, Object>) [end]");
413 private FrameCbetmShort buildFrameCbetmShort(final Map<Label, Object> frameValues) throws InvalidFrameException {
414 logger.trace("buildFrameCbetmShort(Map<Label, Object>) [start]");
415 FrameCbetmShort frame = new FrameCbetmShort();
416 frame.setAdco(getRequiredLabelValue(Label.ADCO, frameValues));
417 frame.setIinst1(getRequiredLabelValue(Label.IINST1, frameValues));
418 frame.setIinst2(getRequiredLabelValue(Label.IINST2, frameValues));
419 frame.setIinst3(getRequiredLabelValue(Label.IINST3, frameValues));
420 frame.setAdir1(getOptionalLabelValue(Label.ADIR1, frameValues));
421 frame.setAdir2(getOptionalLabelValue(Label.ADIR2, frameValues));
422 frame.setAdir3(getOptionalLabelValue(Label.ADIR3, frameValues));
423 logger.trace("buildFrameCbetmShort(Map<Label, Object>) [end]");
427 private FrameCbemm buildFrameCbemm(final Map<Label, Object> frameValues) throws InvalidFrameException {
428 logger.trace("buildFrameCbemm(Map<Label, Object>) [start]");
429 final FrameCbemm frameCbemm;
430 String optionTarif = getRequiredLabelValue(Label.OPTARIF, frameValues);
431 if ("BASE".equals(optionTarif)) {
432 frameCbemm = buildFrameCbemmBaseOption(frameValues);
433 } else if ("HC..".equals(optionTarif)) {
434 frameCbemm = buildFrameCbemmHcOption(frameValues);
435 } else if ("EJP.".equals(optionTarif)) {
436 frameCbemm = buildFrameCbemmEjpOption(frameValues);
437 } else if (optionTarif.startsWith("BBR") && optionTarif.length() == 4) {
438 ProgrammeCircuit1 prgCircuit1 = convertProgrammeCircuit1(optionTarif.charAt(3));
439 ProgrammeCircuit2 prgCircuit2 = convertProgrammeCircuit2(optionTarif.charAt(3));
440 frameCbemm = buildFrameCbemmTempoOption(frameValues, prgCircuit1, prgCircuit2);
442 final String error = String.format("The option Tarif '%s' is not supported", optionTarif);
443 throw new InvalidFrameException(error);
445 logger.trace("buildFrameCbemm(Map<Label, Object>) [end]");
449 private void setCbemmCommonFrameFields(final FrameCbemm frame, final Map<Label, Object> frameValues)
450 throws InvalidFrameException {
451 logger.trace("setCbemmCommonFrameFields(Frame, Map<Label, Object>) [start]");
452 frame.setAdco(getRequiredLabelValue(Label.ADCO, frameValues));
453 frame.setIsousc(getRequiredLabelValue(Label.ISOUSC, frameValues));
454 frame.setIinst(getRequiredLabelValue(Label.IINST, frameValues));
455 frame.setImax(getOptionalLabelValue(Label.IMAX, frameValues));
456 frame.setPtec(getRequiredLabelValue(Label.PTEC, frameValues));
457 frame.setAdps(getOptionalLabelValue(Label.ADPS, frameValues));
458 frame.setMotdetat(getRequiredLabelValue(Label.MOTDETAT, frameValues));
459 logger.trace("setCbemmCommonFrameFields(Frame, Map<Label, Object>) [end]");
462 private FrameCbemmBaseOption buildFrameCbemmBaseOption(final Map<Label, Object> frameValues)
463 throws InvalidFrameException {
464 logger.trace("buildFrameCbemmBaseOption(Map<Label, Object>) [start]");
465 FrameCbemmBaseOption frame = new FrameCbemmBaseOption();
466 setCbemmCommonFrameFields(frame, frameValues);
467 setFrameBaseOptionFields(frame, frameValues);
468 logger.trace("buildFrameCbemmBaseOption(Map<Label, Object>) [end]");
472 private FrameCbemmHcOption buildFrameCbemmHcOption(final Map<Label, Object> frameValues)
473 throws InvalidFrameException {
474 logger.trace("buildFrameCbemmHcOption(Map<Label, Object>) [start]");
475 FrameCbemmHcOption frame = new FrameCbemmHcOption();
476 setCbemmCommonFrameFields(frame, frameValues);
477 setFrameHcOptionFields(frame, frameValues);
478 logger.trace("buildFrameCbemmHcOption(Map<Label, Object>) [end]");
482 private FrameCbemmEjpOption buildFrameCbemmEjpOption(final Map<Label, Object> frameValues)
483 throws InvalidFrameException {
484 logger.trace("buildFrameCbemmEjpOption(Map<Label, Object>) [start]");
485 FrameCbemmEjpOption frame = new FrameCbemmEjpOption();
486 setCbemmCommonFrameFields(frame, frameValues);
487 setFrameEjpOptionFields(frame, frameValues);
488 logger.trace("buildFrameCbemmEjpOption(Map<Label, Object>) [end]");
492 private FrameCbemmTempoOption buildFrameCbemmTempoOption(final Map<Label, Object> frameValues,
493 ProgrammeCircuit1 prgCircuit1, ProgrammeCircuit2 prgCircuit2) throws InvalidFrameException {
494 logger.trace("buildFrameCbemmTempoOption(Map<Label, Object>) [start]");
495 FrameCbemmTempoOption frame = new FrameCbemmTempoOption();
496 setCbemmCommonFrameFields(frame, frameValues);
497 setFrameTempoOptionFields(frame, frameValues, prgCircuit1, prgCircuit2);
498 logger.trace("buildFrameCbemmTempoOption(Map<Label, Object>) [end]");
502 private FrameCbemmEvolutionIcc buildFrameCbemmEvolutionIcc(final Map<Label, Object> frameValues)
503 throws InvalidFrameException {
504 logger.trace("buildFrameCbemmEvolutionIcc(Map<Label, Object>) [start]");
505 final FrameCbemmEvolutionIcc frameCbemmEvoIcc;
506 String optionTarif = getRequiredLabelValue(Label.OPTARIF, frameValues);
507 if ("BASE".equals(optionTarif)) {
508 frameCbemmEvoIcc = buildFrameCbemmEvolutionIccBaseOption(frameValues);
509 } else if ("HC..".equals(optionTarif)) {
510 frameCbemmEvoIcc = buildFrameCbemmEvolutionIccHcOption(frameValues);
511 } else if ("EJP.".equals(optionTarif)) {
512 frameCbemmEvoIcc = buildFrameCbemmEvolutionIccEjpOption(frameValues);
513 } else if (optionTarif.startsWith("BBR") && optionTarif.length() == 4) {
514 ProgrammeCircuit1 prgCircuit1 = convertProgrammeCircuit1(optionTarif.charAt(3));
515 ProgrammeCircuit2 prgCircuit2 = convertProgrammeCircuit2(optionTarif.charAt(3));
516 frameCbemmEvoIcc = buildFrameCbemmEvolutionIccTempoOption(frameValues, prgCircuit1, prgCircuit2);
518 final String error = String.format("The option Tarif '%s' is not supported", optionTarif);
519 throw new InvalidFrameException(error);
522 logger.trace("buildFrameCbemmEvolutionIcc(Map<Label, Object>) [end]");
523 return frameCbemmEvoIcc;
526 private void setCbemmEvolutionIccCommonFrameFields(final FrameCbemmEvolutionIcc frame,
527 final Map<Label, Object> frameValues) throws InvalidFrameException {
528 logger.trace("setCbemmEvolutionIccCommonFrameFields(Frame, Map<Label, Object>) [start]");
529 setCbemmCommonFrameFields(frame, frameValues);
530 frame.setPapp(getRequiredLabelValue(Label.PAPP, frameValues));
531 logger.trace("setCbemmEvolutionIccCommonFrameFields(Frame, Map<Label, Object>) [end]");
534 private FrameCbemmEvolutionIccBaseOption buildFrameCbemmEvolutionIccBaseOption(final Map<Label, Object> frameValues)
535 throws InvalidFrameException {
536 logger.trace("buildFrameCbemmEvolutionIccBaseOption(Map<Label, Object>) [start]");
537 FrameCbemmEvolutionIccBaseOption frame = new FrameCbemmEvolutionIccBaseOption();
538 setCbemmEvolutionIccCommonFrameFields(frame, frameValues);
539 setFrameBaseOptionFields(frame, frameValues);
540 logger.trace("buildFrameCbemmEvolutionIccBaseOption(Map<Label, Object>) [end]");
544 private FrameCbemmEvolutionIccHcOption buildFrameCbemmEvolutionIccHcOption(final Map<Label, Object> frameValues)
545 throws InvalidFrameException {
546 logger.trace("buildFrameCbemmEvolutionIccHcOption(Map<Label, Object>) [start]");
547 FrameCbemmEvolutionIccHcOption frame = new FrameCbemmEvolutionIccHcOption();
548 setCbemmEvolutionIccCommonFrameFields(frame, frameValues);
549 setFrameHcOptionFields(frame, frameValues);
550 logger.trace("buildFrameCbemmEvolutionIccHcOption(Map<Label, Object>) [end]");
554 private FrameCbemmEvolutionIccTempoOption buildFrameCbemmEvolutionIccTempoOption(
555 final Map<Label, Object> frameValues, ProgrammeCircuit1 prgCircuit1, ProgrammeCircuit2 prgCircuit2)
556 throws InvalidFrameException {
557 logger.trace("buildFrameCbemmEvolutionIccTempoOption(Map<Label, Object>) [start]");
558 FrameCbemmEvolutionIccTempoOption frame = new FrameCbemmEvolutionIccTempoOption();
559 setCbemmEvolutionIccCommonFrameFields(frame, frameValues);
560 setFrameTempoOptionFields(frame, frameValues, prgCircuit1, prgCircuit2);
561 logger.trace("buildFrameCbemmEvolutionIccTempoOption(Map<Label, Object>) [end]");
565 private FrameCbemmEvolutionIccEjpOption buildFrameCbemmEvolutionIccEjpOption(final Map<Label, Object> frameValues)
566 throws InvalidFrameException {
567 logger.trace("buildFrameCbemmEvolutionIccEjpOption(Map<Label, Object>) [start]");
568 FrameCbemmEvolutionIccEjpOption frame = new FrameCbemmEvolutionIccEjpOption();
569 setCbemmEvolutionIccCommonFrameFields(frame, frameValues);
570 setFrameEjpOptionFields(frame, frameValues);
571 logger.trace("buildFrameCbemmEvolutionIccEjpOption(Map<Label, Object>) [end]");
575 private void setFrameBaseOptionFields(final FrameBaseOption frameBaseOption, final Map<Label, Object> frameValues)
576 throws InvalidFrameException {
577 logger.trace("setFrameBaseOptionFields(FrameBaseOption) [start]");
578 frameBaseOption.setBase(getRequiredLabelValue(Label.BASE, frameValues));
579 logger.trace("setFrameBaseOptionFields(FrameBaseOption) [end]");
582 private void setFrameHcOptionFields(final FrameHcOption frameHcOption, final Map<Label, Object> frameValues)
583 throws InvalidFrameException {
584 logger.trace("setFrameHcOptionFields(FrameHcOption) [start]");
585 frameHcOption.setHchc(getRequiredLabelValue(Label.HCHC, frameValues));
586 frameHcOption.setHchp(getRequiredLabelValue(Label.HCHP, frameValues));
587 frameHcOption.setHhphc(getRequiredLabelValue(Label.HHPHC, frameValues));
588 logger.trace("setFrameHcOptionFields(FrameHcOption) [end]");
591 private void setFrameEjpOptionFields(final FrameEjpOption frameEjpOption, final Map<Label, Object> frameValues)
592 throws InvalidFrameException {
593 logger.trace("setFrameEjpOptionFields(FrameEjpOption) [start]");
594 frameEjpOption.setEjphn(getRequiredLabelValue(Label.EJPHN, frameValues));
595 frameEjpOption.setEjphpm(getRequiredLabelValue(Label.EJPHPM, frameValues));
596 frameEjpOption.setPejp(getOptionalLabelValue(Label.PEJP, frameValues));
597 logger.trace("setFrameEjpOptionFields(FrameEjpOption) [end]");
600 private void setFrameTempoOptionFields(final FrameTempoOption frameTempoOption,
601 final Map<Label, Object> frameValues, ProgrammeCircuit1 prgCircuit1, ProgrammeCircuit2 prgCircuit2)
602 throws InvalidFrameException {
603 logger.trace("setFrameTempoOptionFields(FrameTempoOption) [start]");
604 frameTempoOption.setBbrhpjr(getRequiredLabelValue(Label.BBRHPJR, frameValues));
605 frameTempoOption.setBbrhcjr(getRequiredLabelValue(Label.BBRHCJR, frameValues));
606 frameTempoOption.setBbrhpjw(getRequiredLabelValue(Label.BBRHPJW, frameValues));
607 frameTempoOption.setBbrhcjw(getRequiredLabelValue(Label.BBRHCJW, frameValues));
608 frameTempoOption.setBbrhpjb(getRequiredLabelValue(Label.BBRHPJB, frameValues));
609 frameTempoOption.setBbrhcjb(getRequiredLabelValue(Label.BBRHCJB, frameValues));
610 frameTempoOption.setDemain(getOptionalLabelValue(Label.DEMAIN, frameValues));
611 frameTempoOption.setHhphc(getRequiredLabelValue(Label.HHPHC, frameValues));
612 frameTempoOption.setProgrammeCircuit1(prgCircuit1);
613 frameTempoOption.setProgrammeCircuit2(prgCircuit2);
614 logger.trace("setFrameTempoOptionFields(FrameTempoOption) [end]");
617 @SuppressWarnings("unchecked")
618 private <T> T getRequiredLabelValue(Label label, final Map<Label, Object> frameValues)
619 throws InvalidFrameException {
620 if (!frameValues.containsKey(label)) {
621 final String error = String.format("The required label '%1$s' is missing in frame", label);
622 throw new InvalidFrameException(error);
625 return (T) frameValues.get(label);
628 @SuppressWarnings("unchecked")
629 private <T> T getOptionalLabelValue(Label label, final Map<Label, Object> frameValues) {
630 return (T) frameValues.get(label);
633 private ProgrammeCircuit1 convertProgrammeCircuit1(char value) {
634 String prgCircuit1 = computeProgrammeCircuitBinaryValue(value).substring(3, 5);
635 switch (prgCircuit1) {
637 return ProgrammeCircuit1.A;
639 return ProgrammeCircuit1.B;
641 return ProgrammeCircuit1.C;
643 final String error = String.format("Programme circuit 1 '%s' is unknown", prgCircuit1);
644 throw new IllegalStateException(error);
648 private ProgrammeCircuit2 convertProgrammeCircuit2(char value) {
649 String prgCircuit2 = computeProgrammeCircuitBinaryValue(value).substring(5, 8);
650 switch (prgCircuit2) {
652 return ProgrammeCircuit2.P0;
654 return ProgrammeCircuit2.P1;
656 return ProgrammeCircuit2.P2;
658 return ProgrammeCircuit2.P3;
660 return ProgrammeCircuit2.P4;
662 return ProgrammeCircuit2.P5;
664 return ProgrammeCircuit2.P6;
666 return ProgrammeCircuit2.P7;
668 final String error = String.format("Programme circuit 2 '%s' is unknown", prgCircuit2);
669 throw new IllegalStateException(error);
673 private String computeProgrammeCircuitBinaryValue(char value) {
674 return String.format("%8s", Integer.toBinaryString(value)).replace(' ', '0');
677 private void rethrowTaskExecutionException(ExecutionException e)
678 throws InvalidFrameException, IOException, TimeoutException {
679 Throwable cause = e.getCause();
680 if (cause instanceof InvalidFrameException) {
681 throw (InvalidFrameException) cause;
682 } else if (cause instanceof IOException) {
683 throw (IOException) cause;
684 } else if (cause instanceof TimeoutException) {
685 throw (TimeoutException) cause;
687 throw new IllegalStateException(e);